js继续紧张包罗原型链继续、借用构造函数继续、组合继续(原型链+构造函数)、组合继续优化、寄生组合继续5中方式,后3中都是由前两个组合优化而来,以是要先相识原型及原型链相关内容。
1、原型对象及原型链
在js中,齐备皆对象,以是原型也是一个对象,称为原型对象。
在js中,每个函数范例的数据,都有一个prototype的属性,该属性所指向的对象就是原型对象。对于原型对象而言,其constructor属性则指回构造函数(构造函数与平凡函数本质上没有差异,只是可以用new 关键字创建对象)。原型对象最紧张的作用是存放对象的共有属性和共有方法。
构造函数类似java中的类,每次创建实例对象时,都会重复创建一次构造函数内里的属性和方法,这就有些浪费了。如果把共有属性和方法界说到构造函数的原型对象里,各个实例对象直接原型对象的方法即可(通过原型链实现),制止浪费空间。
示例:
function Person(name, age) { this.name = name; this.age = age; this.say = function () { console.log("构造函数方法--- ", this.name, this.age); }}Person.prototype.sex = "女"erson.prototype.color = ["yellow", "red", "blue"]erson.prototype.hello = function () { console.log("原型对象方法---- hello ", this.sex);}let p1 = new Person("dlj1", 20);let p2 = new Person("dlj2", 30);console.log("p1 is ", p1);console.log("p2 is ", p2);p2.hello();console.log("p1.color ", p1.color.join("/"));console.log("person 原型对象 ", p1.__proto__, p1.constructor.prototype == p1.__proto__, p1.__proto__ == Person.prototype, Person.prototype.constructor == p1.constructor);console.log("person 原型对象上级原型对象包罗join方法 ", p1.color.__proto__.hasOwnProperty("join"));
从上面的示例可以看出,实例对象中没有sex属性和hello方法,但是并不影响p2调用hello方法,就是由于当实例对象内没有调用的方法或属性时,就会向对应构造函数的原型对象中查找,如果原型对象中也没有,则会继续向原型对象的原型对象上查找,直到最顶级Object.prototype(全部原型对象都是object的实例),如果最顶级没有找到则返回undefined,这个顺着proto逐步向上查找,形成了像链条一样的布局就叫原型链(也叫隐式原型链)。比方p1的原型对象中没有join方法,p1.color可以调用join就是由于上级原型对象(array)中包罗join方法。
2、继续方式先容
1)原型链继续
将父类实例作为子类原型,由于方法界说在父类原型上,复用了父类构造函数的方法,以是实现了方法复用。
示例:
function Person(name) { this.name = name || "父类"; this.say = function () { console.log("父类构造函数方法--- ", this.name); }}Person.prototype.sex = "女"erson.prototype.color = ["yellow", "red", "blue"]erson.prototype.hello = function () { console.log("父类原型对象方法---- hello ", this.sex);}function Child(age) { this.age = age;}Child.prototype = new Person("dlj");Child.prototype.constrctor = Child;Child.prototype.address = "北京";Child.prototype.cHello = function () { console.log("子类原型对象方法---- chello ", this.sex);}let chd1 = new Child("22");console.log(chd1.say());console.log(chd1.hello());console.log(chd1.cHello());console.log("age name sex --- ", chd1.age, chd1.name, chd1.sex);console.log("chd1 ", chd1);console.log("chd1.__proto__ ", chd1.__proto__);console.log("chd1.__proto__.__proto__ ", chd1.__proto__.__proto__);console.log("chd1.__proto__ has color is ", chd1.__proto__.hasOwnProperty("color"));chd1.color.push("aa");console.log(new Person().color);
从上面的示例可以看出:
①子类原型对象中包罗了父类构造函数中的属性、方法和子类原型对象中的属性、方法,但是不包罗父类原型对象中方法和属性color。
②子类实例能调用父类原型对象中的hello方法 color属性则是由于父类原型对象是子类原型对象的原型对象。
③父类原型对象中引用范例数据会在各个实例中共享,一个子类实例修改父类原型对象中引用属性的值(color),其他实例对象调用该引用属性时,值也是修改后的。
④只能继续一个父类,不能多继续。
留意:子类原型对象上界说属性和方法必须在Child.prototype = new Person("dlj")之后,否则子类原型对象上独有的属性和方法会丢失。
2)借用构造函数
借用父类的构造函数来加强子类实例,相当于复制父类的实例属性给子类,以是子类实例之间相互独立,不共享父类的引用属性,并可实现多继续(call或apply).
示例:
function Person1(name) { this.name = name || "父类"; this.job = ["job1", "job2"]; this.say = function () { console.log("父类构造函数方法--- ", this.name); }}Person1.prototype.sex = "女"erson1.prototype.color = ["yellow", "red", "blue"]erson1.prototype.hello = function () { console.log("父类原型对象方法---- hello ", this.sex);}function Person2(name) { this.name2 = name || "父类2"; this.job2 = ["job1", "job2"]; this.say2 = function () { console.log("父类构造函数方法--- ", this.name2); }}Person2.prototype.sex2 = "女"erson2.prototype.color2 = ["yellow", "red", "blue"]erson2.prototype.hello2 = function () { console.log("父类原型对象方法---- hello ", this.sex2);}function Child(name, age) { Person1.call(this, name); Person2.call(this, name); this.age = age; this.hello3 = function () { console.log("子类构造函数方法--- ", this.age); }}Child.prototype.address = "北京";Child.prototype.cHello = function () { console.log("子类原型对象方法---- chello ");}let chd1 = new Child("dlj", 11);let chd2 = new Child("dlj1", 111);console.log("chi1 is ", chd1);console.log("chd1.__proto__ ", chd1.__proto__);console.log("chd1.__proto__.__proto__ ", chd1.__proto__.__proto__);chd1.job2.push("job3");console.log("chd1.job2 ", chd1.job2, chd2.job2);console.log("chd1.sex ", chd1.sex);//undefined 子类实例未继续父类原型中属性chd1.say2();//console.log("chd1.hello ", chd1.hello());//报错 chd1.hello is not a function 子类实例未继续父类原型中和方法
从示例中可以看出:
①由于通过父类构造函数继续,不涉及父类原型对象,以是父类原型对象中的属性和方法不可用(无法方法复用),子类只能调用自身和继续父类构造函数中的方法和属性。
②子类可以继续多个父类,实现多继续。
③父类中引用属性(job2)在子类实例中相互独立。
3)组合继续
将借用构造函数和原型链继续组合使用,既可以实现父类原型对象方法的复用,有可以使父类中引用属性相互独立并实现多继续。
function Person1(name) { this.name = name || "父类"; this.job = ["job1", "job2"]; this.say = function () { console.log("父类构造函数方法--- ", this.name); }}Person1.prototype.sex = "女"erson1.prototype.color = ["yellow", "red", "blue"]erson1.prototype.hello = function () { console.log("父类原型对象方法---- hello ", this.sex);}function Person2(name) { this.name2 = name || "父类2"; this.job2 = ["job1", "job2"]; this.say2 = function () { console.log("父类构造函数方法--- ", this.name2); }}Person2.prototype.sex2 = "女";Person2.prototype.color2 = ["yellow", "red", "blue"];Person2.prototype.hello2 = function () { console.log("父类原型对象方法---- hello ", this.sex2);}function Child(name, age) { Person1.call(this, name); Person2.call(this, name); this.age = age; this.hello3 = function () { console.log("子类构造函数方法--- ", this.age); }}Child.prototype = new Person1("dlj");Child.prototype.constrctor = Child;Child.prototype.address = "北京";Child.prototype.cHello = function () { console.log("子类原型对象方法---- chello ");}let chd1 = new Child("dlj", 11);let chd2 = new Child("dlj1", 111);console.log("chi1 is ", chd1);console.log("chd1.__proto__ ", chd1.__proto__);console.log("chd1.__proto__.__proto__ ", chd1.__proto__.__proto__);chd1.job2.push("job3");console.log("chd1.job2 ", chd1.job2, chd2.job2);console.log("chd1.sex ", chd1.sex);chd1.say2();chd1.hello();
从示例可以看出子类实例中属性、方法与子类原型对象的属性和方法有重复,这也算是组合继续唯一的缺点了。
4)组合继续优化
3)的实现方式之以是造成属性或方法重复,是由于调用了两次创建父类实例的方法,这是将父类原型对象直接赋值给子类原型可以制止一次创建父类实例方法的调用,从而办理重复的问题。将上个示例中Child.prototype = new Person1("dlj") 改为Child.prototype = Person1.prototype(或者改为Child.prototype = Object.create(Person1.prototype));其他不动,与3)相比,重复属性和方法被去掉了。
示例:
改为Child.prototype = Object.create(Person1.prototype))时
接3)中示例console.log("Child.prototype.__proto__ ", Child.prototype.__proto__);console.log("Person1.prototype ", Person1.prototype);console.log(Child.prototype.__proto__ == Person1.prototype);//Child.prototype = new Person1("dlj")时 Child.prototype.__proto__ {}Person1.prototype Person1 { sex: '女', color: [ 'yellow', 'red', 'blue' ], hello: [Function], constrctor: [Function: Child], address: '北京', cHello: [Function]}false//Child.prototype = Object.create(Person1.prototype)时Child.prototype.__proto__ Person1 { sex: '女', color: [ 'yellow', 'red', 'blue' ], hello: [Function]}Person1.prototype Person1 { sex: '女', color: [ 'yellow', 'red', 'blue' ], hello: [Function]}trueObject.create(object,propertiesObject)方法创建一个新对象,使用第一个参数来提供创建对象的proto(以第一个参数作为新对象的构造函数的原型),第二个可选参数,是添加到新创建对象的属性。 |