一、优化字面量的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 var obj1 = { name:'张三' , age:24 }; var animal={ name:'翠花' , age:3 }; function CreatePerson(name,age){ this.name = name; this.age = age; } var obj2 = new CreatePerson('李四' ,28)
obj1和animal的constructor都是指向object,而obj2的constructor是指向CreatePerson的构造函数的,因为是构造函数所创建的实例
1 2 3 4 5 6 console.log(obj1.constructor) console.log(animal.constructor) console.log(obj2.constructor); 优化字面量 function Box (){}
改变原型里面的属性,覆盖了本身的prototype,就没有了constructor
1 2 3 4 5 6 7 8 9 10 11 Box.prototype = { constructor:Box, name:'jj' , age:22 } var person = new Box(); console.log(person) //undefined因为对象里面的prototype不能访问 console.log(person.prototype) console.log(Box.prototype) console.log(person.constructor)
二、原型 什么是原型? 原被型是当构造函数被调由系统用时创把建出来的一个实例,我们可以利用原型这个实例把对象中相同的属性和方法添加到原型中,达到节约内存的目的。 如何获取到原型? 在构造函数中有一个“prototype”的属性,该属性就指向了构造函数的原型,函数名.prototype prototype属性:通过这个属性能获取到对应的原型.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function CreatePreson(name,age,gender){ this.name = name; this.age = age; CreatePreson.prototype.gender = gender; CreatePreson.prototype.sayBye = function (){ console.log('bye' ) } }; //通过函数名下的prototype属性获取函数原型 CreatePreson.prototype.sayHi = function (){ console.log('hi' ) } //创建对象,并调用原型方法 var per1 = new CreatePreson('jio' ,22,'man' )
因为该方法并不是实例方法,所以实例调用这个方式时,在实例中找不到,就再进入实例 的原型中寻找,找到就执行。 对象如何获取它的原型:每个对象都有一个proto 属性,该属性就指向了对象对应的原型,但是该属性我们 无法访问。
1 2 3 4 5 6 7 console.log(per1.__proto__) console.log(CreatePreson.__proto__) per1.sayHi(); per1.sayBye(); console.log(per1.gender) //constructor :原型下的属性,可以通过该属性获取到原型对应的构造函数 console.log(CreatePreson.prototype.constructor)
三、通过构造函数创建对象方法的二次优化 把拥有相同值的属性,方法添加到原型中,把不同的属性和方法添加到各自的对象中
1 2 3 4 5 6 7 8 9 function CreateAnimal(name,age){ //因为动物的名字和年龄各不相同,所以把这两个属性添加到对象中 this.name = name; this.age = age; //因为动物都有吃东西的方法,所以可以放到原型中 CreateAnimal.prototype.eat = function (){ console.log('吃东西' ) } }
四、通过构造函数创建对象方法的三次优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function CreatePerson(name,age){ this.name = name; this.age = age; //每次调用都会在在原型里面创建一次,很累赘。所以用if 来判断,如果原型里面 //有该方法,就不用再往原型里面放了因为每个对象的方法都是相同,所以我们可 //以把相同的方法添加到原型里传统添加方式:每次调用都会给原型添加一次方法 //优化方法:判断原型中是否已经存在要添加的方法,如果没有就添加,否则不添加。 if (typeof(CreatePerson.prototype.sayHi) != 'function' ){ console.log('我被执行了' ) CreatePerson.prototype.sayHi = function (){ console.log('hello' +this.name) } } }
写在外面不太合理的原因,是因为程序由上到下执行,对象还没有调用,就已经把方 法添加到了原型中,有可能用不到方法
1 2 3 4 5 6 7 CreatePerson.prototype.sayHi = function (){ console.log('hello' ) } var per1 = new CreatePerson('李四' ,22) per1.sayHi(); var per2 = new CreatePerson('向三' ,11) per2.sayHi();
五、原型操作 定义一个是构造函数,有一个实例属性:name,有一个原型属性age
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function CreatePerson(name,age){ this.name = name; CreatePerson.prototype.age=age; } //调用构造函数创建对象 var per1 = new CreatePerson('jio' ,22); console.log(per1); console.log(per1.age) var per2 = new CreatePerson('margi' ,18); //原型修改会被提升,所以上面打印就是console.log(per1)的__proto__中就可以看到. console.log(per1); console.log(per2); console.log(per1.age) //定义一个是构造函数,有一个实例属性:name,有一个原型属性age function CreatePerson (name) { this.name = name; CreatePerson.prototype.age = 18; } //调用构造函数创建对象 var per1 = new CreatePerson('宁哥' ); var per2 = new CreatePerson('凯哥' ); //in 操作符,只能判断对象中有没有该属性,无法判断属性是实例属性还是原型属性 console.log('name' in per1);//true console.log('age' in per1);//true //hasOwnProperty方法:判断属性是否是对象的实例属性,是则返回true ,不是或没有该属性,返回false 。 console.log(per1.hasOwnProperty('name' ));//true console.log(per1.hasOwnProperty('age' ));//false
判断属性是否是原型属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1、判断指定的字符串是对象下的属性 if ('age' in per1) { 2、判断属性是否是原型属性:对hasOwnProperty取反 if (!per1.hasOwnProperty('age' )) { console.log('原型属性' ); } } 简化操作 if ('age' in per1 && !per1.hasOwnProperty('age' )) { console.log('原型属性' ); } 深度简化 CreatePerson.prototype.hello = function hello (proto) { if (proto in this && !this.hasOwnProperty(proto)) { console.log('原型属性' ); } }; per1.hello('age' );
六、call和apply 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function sayHi(name1,name2){ console.log(name1+' ' +name2+' hahah' ); console.log(this) console.log(name) } sayHi(); sayHi.call();//call方法就是用来调用函数的 //第一个参数是可以改变this的指向, //sayHi.call(Object); //sayHi.call(Array); //第二个参数是函数需要的参数 //sayHi.call(Object); //sayHi.call(Array,'jj' ); sayHi.call(null,'jj' ,'hh' );//指向window //apply第二个参数必须是数组类型的 sayHi.apply(Array,['jj' ,'hh' ])
总结 我们既可以使用”()”来调用函数,也可以使用call方法来调用函数 使用call调用执行函数,该方法需要有两大类的参数 第一大类参数是:用来修改要执行的函数中的this指针的指向 第二大类参数是:要执行函数所需要的参数
七、call和apply的继承 创建出第一个函数(父函数\父类)
1 2 3 4 5 6 7 function CreateAnimal (name, age) { this.name = name; this.age = age; this.sayHi = function () { alert('hello' ); } }
创建第二个函数:这个函数获取到的对象也有第一个函数中的属性和方法,我们可 以将这些相同的属性和方法从第一个函数中获取到,这样第二个函数就不必单独再 写一次赋值操作。
因为第二个函数从第一个函数中拿到了全部属性、方法则我们认为第二个函数是第 一个函数的子类。
通过call 方法实现继承:修改第一个函数中this的指向,改为person对象, 这样在执行第一个函数时,就给person对象添加了属性和方法
1 2 3 4 5 6 7 8 function CreatePerson (name, age) { 利用call调用执行第一个函数,并修改他的指针指向为person CreateAnimal.call(this, name, age); 添加本函数独有的属性和方法 this.sayBye = function (){ alert('bye-bye' ); } }
八、原型继承 父函数
1 2 3 4 5 6 7 function CreateAnimal (name,age) { this.name = name; this.age = age; this.sayHi = function () { alert('hello' ); }; }
子函数
1 2 3 4 5 6 function CreatePerson (gender) { this.gender = gender; this.sayBye = function () { alert('bye' ); }; }
1、创建一个父类的对象,把这个对象当做子类的原型
1 CreatePerson.prototype = new CreateAnimal('宁哥' , 24);
2、因为此时子类原型中的constructor指向的是父类构造函数,所以创建出的对象也 将指向Animal这个函数。为了让constructor的指向正确,需要修改Person原型中的constructor
1 CreatePerson.prototype.constructor = CreatePerson;
3、创建并使用子类对象
1 2 3 4 var per = new CreatePerson('男' ); console.log(per.name); console.log(per.age); console.log(per.gender);
九、组合继承 父类函数:属性保存在实例中,方法保存在原型中
1 2 3 4 5 6 7 function CreateAnimal(name,age){ this.name = name; this.age = age; CreateAnimal.prototype.sayHi = function (){ console.log('hello' ); } }
子类函数:通过call继承实例相关
1 2 3 4 5 6 7 8 function CreatePerson(name,age,gender){ //使用call从父类继承属性和方法只是this的指向的问题,把父类中的this强变成子类中的this,也就是子类的对象,而sayHi方法不是在this上,而是在原型上 CreateAnimal.apply(this,arguments) this.gender = gender; CreateAnimal.prototype.sayBye = function (){ console.log('bye' ); } }
因为父类有添加在原型中方法,通过call获取不到,所以需要使用原型来获取这 些方法 因为只是想获取到父类的原型方法,所以不需要给父类的属性传参
1 2 3 4 5 6 7 8 CreatePerson.prototype = new CreateAnimal(); 把子类的constructor属性改回来 CreatePerson.prototype.constructor = CreatePerson var person= new CreatePerson('jj' ,22,'man' ); console.log(person.name) console.log(person.age) console.log(person.gender) person.sayHi();