We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本文总结自红宝书
无法识别对象类型
function createPerson(name, age) { var o = new Object(); o.name = name; o.age = age; o.sayName = function() { alert(this.name); } return o; } var person = createPerson('Luan', 20);
解决了对象类型识别的问题;
不同实例上的同名函数是不相等的,每个方法都要在每个实例上重新创建一遍。
function Person(name, age) { this.name = name; this.age = age; this.sayName = function() { alert(this.name); } } var person1 = new Person('Luan', 20); var person2 = new Person('Wang', 21); person1.constructor === Person; // true person1 instanceof Object; // true person1 instanceof Person; // true person1.sayName === person2.sayName; // false
使用new操作符,会经历四个步骤:
模拟实现 new:
function create() { // 创建一个空的对象 let obj = new Object() // 获得构造函数 let Con = [].shift.call(arguments) // 链接到原型 obj.__proto__ = Con.prototype // 绑定 this,执行构造函数 let result = Con.apply(obj, arguments) // 确保 new 出来的是个对象 return typeof result === 'object' ? result : obj }
原型对象的属性和方法由所有实例共享。
省略了构造函数传参初始化参数,所有实例在默认情况下都将取得相同的属性值;
原型中的所有属性都是被实例共享的,包括引用类型,导致一个实例修改引用类型属性后,其他实例的值也会跟着改变。
function Person() { } Person.protptype.name = 'Luan'; Person.prototype.age = 20; Person.prototype.sayName = function() { alert(this.name); } var person = new Person();
每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向原型对象,原型对象包含由特定类型的所有实例共享的属性和方法。
prototype
在默认情况下,所有原型对象会自动获得一个constructor(构造函数)属性,该属性是一个指向prototype属性所在函数的指针。
constructor
Person.prototype.constructor === Person; // true
调用构造函数创建的实例,内部将包含一个[[Prototype]]指针(该指针在Firefox、Safari、Chrome中可以通过__propto__属性访问,其他实现中不可见),指向构造函数的原型对象。
[[Prototype]]
__propto__
注意:[[Prototype]]指针存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。
person.__propto__ === Person.proptotype; // true
可以通过实例访问原型对象中的值,但不能重写。当为实例添加一个属性时,这个属性会屏蔽原型对象中保存的同名属性;换句话说,实例添加同名属性只会阻止访问原型对象中的同名属性,但不会修改那个属性。
hasOwnProperty()方法可以检测一个属性是存在于实例中,还是原型对象中,存在于实例中时返回true。
hasOwnProperty()
true
for-in循环返回的属性是所有能通过对象访问的、可枚举的属性,既包括实例中的也包括原型对象中的。
for-in
function Person() { } // 重写了prototype对象,此时constructor属性不再指向Person Person.protptype = { // 手动绑定constructor属性使其指向Person // 但会导致它的[[Enumerable]]特性被设为true,默认为false // 可以通过Object.defineProperty()设置为false constructor: Person, name: 'Luan', age: 20, sayName: function() { alert(this.name); }, } var person = new Person();
创建自定义类型最常见的方式;
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
function Person(name, age) { this.name = name; this.age = age; this.friends = ['Ming', 'Yang']; } Person.protptype = { constructor: Person, sayName: function() { alert(this.name); }, } var person1 = new Person('Luan', 20); var person2 = new Person('Wang', 21); person1.friends === person2.friends; // false person1.sayName === person2.sayName; // true
function Person(name, age) { this.name = name; this.age = age; this.friends = ['Ming', 'Yang']; // 只有在初次调用构造函数时才会执行 if (typeof this.sayName !== 'function') { Person.prototype.sayName = function() { alert(this.name); } } }
无法使用instanceof操作符确定对象类型,一般不使用
instanceof
function Person(name, age) { var o = new Object(); o.name = name; o.age = age; o.sayName = function() { alert(this.name); } return o; } var person = new Person('Luan', 20);
所谓稳妥对象指的是没有公共属性,其方法也不引用this的对象
无法使用instanceof操作符确定对象类型
function Person(name, age) { var o = new Object(); o.sayName = function() { alert(name); } return o; } var person = Person('Luan', 20); // 除了使用sayName方法外,没有别的办法访问其数据 person.sayName(); // 'Luan'
继承主要依靠原型链来实现。
超类型的实例属性会变成子类的原型属性,而包含引用类型的原型属性会被所有实例共享。
没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; } function SubType() { this.subProperty = false; } // 继承SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function() { return this.subProperty; } var instance = new SubType(); instance.getSuperValue(); // true // SubType的原型对象指向了SuperType的原型对象 // 而SuperType的原型对象的constructor指向SuperType instance.constructor === SuperType; // true
所有引用类型都默认继承了Object,所有函数的默认原型都是Object的实例
Object
给子类原型添加方法的代码一定要放在替换原型的语句之后。
方法都在构造函数中定义,没有实现函数复用;
超类型原型中定义的方法,对子类是不可见的。
function SuperType(name) { this.name = name; } function SubType(name, age) { // 继承了SuperType SuperType.call(this, name); this.age = age; }
最常用的继承模式;
使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
function SuperType(name) { this.name = name; } SuperType.prototype.sayName = function() { alert(this.name); } function SubType(name, age) { // 继承属性 SuperType.call(this, name); // 第二次调用超类型构造函数 this.age = age; } // 继承方法 SubType.prototype = new SuperType(); // 第一次调用超类型构造函数 SubType.prototype.sayAge = function() { alert(this.age); }
第一次调用SuperType构造函数时,SubType.prototype会得到name属性,即SuperType的实例属性;第二次调用SuperType构造函数时,在新对象上创建了实例属性name和age,屏蔽了原型对象中的同名属性。
SuperType
SubType.prototype
name
age
借助原型基于已有的对象创建新的对象,相当于创建了已有对象的副本,引用类型属性也会共享。
function object(o) { function F() {} F.prototype = o; return new F(); }
ES5通过新增Object.create()方法规范化了原型式继承。
Object.create()
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回增强后的对象。
函数不能复用。
function createAnother(o) { var clone = object(o); o.sayHi = function() { alert('Hi'); }; return clone; }
最理想的继承范式。
组合继承无论在什么情况下,都会调用两次超类型构造函数。一次是在创建子类型原型的时候,一次是在子类型构造函数内部。
寄生组合式继承,通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); // 创建对象 prototype.constructor = subType; // 增强对象 subType.prototype = prototype; // 指定对象 } function SuperType(name) { this.name = name; } SuperType.prototype.sayName = function() { alert(this.name); } function SubType(name, age) { SuperType.call(this, name); this.age = age; } // 注意!!! inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function() { alert(this.age); }
只调用了一次SuperType的构造函数,并因此避免了在SubType.prototype上创建不必要的、多余的属性。同时,原型链保持不变,能够正常使用instanceof。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
对象&继承
本文总结自红宝书
一、创建对象
1. 工厂模式
1.1 优缺点
无法识别对象类型
1.2 实现
2. 构造函数模式
2.1 优缺点
解决了对象类型识别的问题;
不同实例上的同名函数是不相等的,每个方法都要在每个实例上重新创建一遍。
2.2 实现
2.3 new 操作符
使用new操作符,会经历四个步骤:
模拟实现 new:
3. 原型模式
3.1 优缺点
原型对象的属性和方法由所有实例共享。
省略了构造函数传参初始化参数,所有实例在默认情况下都将取得相同的属性值;
原型中的所有属性都是被实例共享的,包括引用类型,导致一个实例修改引用类型属性后,其他实例的值也会跟着改变。
3.2 实现
3.3 原型
每个函数都有一个
prototype
(原型)属性,这个属性是一个指针,指向原型对象,原型对象包含由特定类型的所有实例共享的属性和方法。3.4 原型对象
在默认情况下,所有原型对象会自动获得一个
constructor
(构造函数)属性,该属性是一个指向prototype属性所在函数的指针。Person.prototype.constructor === Person; // true
调用构造函数创建的实例,内部将包含一个
[[Prototype]]
指针(该指针在Firefox、Safari、Chrome中可以通过__propto__
属性访问,其他实现中不可见),指向构造函数的原型对象。注意:
[[Prototype]]
指针存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。person.__propto__ === Person.proptotype; // true
可以通过实例访问原型对象中的值,但不能重写。当为实例添加一个属性时,这个属性会屏蔽原型对象中保存的同名属性;换句话说,实例添加同名属性只会阻止访问原型对象中的同名属性,但不会修改那个属性。
hasOwnProperty()
方法可以检测一个属性是存在于实例中,还是原型对象中,存在于实例中时返回true
。for-in
循环返回的属性是所有能通过对象访问的、可枚举的属性,既包括实例中的也包括原型对象中的。3.5 更简单的原型语法
4. 组合使用构造函数模式和原型模式
4.1 优缺点
创建自定义类型最常见的方式;
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
4.2 实现
5. 动态原型模式
6. 寄生构造函数模式
无法使用
instanceof
操作符确定对象类型,一般不使用7. 稳妥构造函数模式
所谓稳妥对象指的是没有公共属性,其方法也不引用this的对象
无法使用
instanceof
操作符确定对象类型二、继承
继承主要依靠原型链来实现。
1. 原型链
1.1 优缺点
超类型的实例属性会变成子类的原型属性,而包含引用类型的原型属性会被所有实例共享。
没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
1.2 实现
所有引用类型都默认继承了
Object
,所有函数的默认原型都是Object
的实例给子类原型添加方法的代码一定要放在替换原型的语句之后。
2. 借用构造函数
方法都在构造函数中定义,没有实现函数复用;
超类型原型中定义的方法,对子类是不可见的。
3. 组合继承
最常用的继承模式;
使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
第一次调用
SuperType
构造函数时,SubType.prototype
会得到name
属性,即SuperType
的实例属性;第二次调用SuperType
构造函数时,在新对象上创建了实例属性name
和age
,屏蔽了原型对象中的同名属性。4. 原型式继承
借助原型基于已有的对象创建新的对象,相当于创建了已有对象的副本,引用类型属性也会共享。
ES5通过新增
Object.create()
方法规范化了原型式继承。5. 寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回增强后的对象。
函数不能复用。
6. 寄生组合式继承
最理想的继承范式。
组合继承无论在什么情况下,都会调用两次超类型构造函数。一次是在创建子类型原型的时候,一次是在子类型构造函数内部。
寄生组合式继承,通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
只调用了一次
SuperType
的构造函数,并因此避免了在SubType.prototype
上创建不必要的、多余的属性。同时,原型链保持不变,能够正常使用instanceof
。The text was updated successfully, but these errors were encountered: