原型
原型本质
当某个对象,承担了为其他对象提供共享属性的职责时,它就成了该对象的 prototype。换句话说,如果不跟其他对象产生关联,就不构成 prototype 这个称谓。
所以,prototype 描述的是两个对象之间的某种关系(其中一个为另外一个提供属性访问权限)
当读取实例属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还没找到,就会继续在原型的原型上找,直到找到最顶层。
proto 、 prototype、 constructor
- __proto__ 和 constructor 是对象独有的。
- prototype 属性是函数独有的(函数也是对象)
函数创建的对象.__proto__ === 函数.prototype函数创建的对象.constructor === 函数(往原型链上找)函数.prototype.constructor === 函数
prototype
当创建函数时,js 会为这个函数自动添加 prototype 你属性,值是一个有 constructor 属性的对象,不是空对象。当把函数当作构造函数调用,那么 js 就会在创建实例的同时,实例继承构造函数的 prototype 的所有属性和方法。
- Object.getPrototypeOf(obj): 访问指定对象的 prototype
- Object.setPrototypeOf(obj, otherObj): 设置指定对象的 prototype 对象
作用是:包含可以给特定类型的所有实例提供共享的属性和方法
__proto__
ES 规范里,prototype 是一个隐式引用,但之前,在一些浏览器中,私自实现了 __proto__ 属性,使得可以通过 obj.__proto__ 这个显式的属性访问,访问到被定义为隐式属性的 prototype。 每个 js 对象一定对应一个原型对象,并从原型对象继承属性和方法继承方法和属性。
- obj.__proto__: 访问指定对象的 prototype
- obj.__proto__ = otherObj: 设置指定对象的 prototype 对象
- __proto__ 属性既不能被
for in,也不能被Object.keys(obj)查找出来
constructor
constructor 返回创建实例对象时构造函数的引用,是一个对象指向一个函数。 每一个原型都有一个 constructor 属性指向关联的构造函数。
function Person() {}
console.log(Person === Person.prototype.constructor)
typeof
用于判断一个变量的类型。返回一个字符串。
| 类型 | 结果 |
|---|---|
| Undefined | "undefined" |
| Boolean | "boolean" |
| Number | "number" |
| String | "string" |
| Functiion | "function" |
| Null | "object" |
| Array | "object" |
| 其他任何对象 | "object" |
null 的 typeof 值是 "object",这是因为 js 底层存储变量类型时,对象的类型标签是 0,由于 null 代表的是空指针(大部分平台值为 0x00),所以类型标签是 0, typeof null 也因此返回 "object"
Object.prototype.toString.call()
返回一个表示该字段的字符串
| 类型 | 结果 |
|---|---|
| Undefined | "[object Undefined]" |
| Boolean | "[object Boolean]" |
| Number | "[object Number]" |
| String | "[object String]" |
| Functiion | "[object Function]" |
| Null | "[object Null]" |
| Array | "[object Array]" |
| 其他任何对象 | "[object Object]" |
instanceof
instanceof 用于检测 func.prototype 属性是否出现在实例对象的原型链上,也就是判断一个实例是否是其父类型或者祖先类型的实例。
实现原理就是 右边变量的 prototype 在左边变量的原型链上即可,所以在查找的过程中,会遍历左边变量的原型链,直到找到右边变量的 prototype,如果没找到,会返回 false。
有几个例子:
function Foo() {}
Object instanceof Object //true Object 本身是一个函数
Function instanceof Function //true
Function instanceof Object //true
Foo instanceof Object //true
Foo instanceof Foo //false Foo.__proto__ = Function.prototype
Foo instanceof Function //true
原型链
每个对象都有一个原型对象,通过__proto__ 指针指向上一个原型,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向null,这种关系称为原型链
先有Object.prototype(原型链顶端),Function.prototype继承Object.prototype而产生,最后,Function和Object和其它构造函数继承Function.prototype而产生。
继承
在 js 中,继承的本质就是复制,即重写原型对象,代之以一个新类型的实例。
- 原型链继承:设置 xxx.prototype = xx
- 构造函数继承:使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类 var xxx = new xx()
- 组合继承:用原型链实现对原型属性和方法的继承,借用构造函数技术来实现实例属性的继承
- 原型式继承:继承的 object 方法本质上是对参数对象的一个浅复制.
var xxx = Object.create(xx) - 寄生式继承:使用原型式继承获得一个目标对象的浅复制,然后增强这个浅复制的能力。
- 寄生组合继承
- ES6 Class Extends: 是一种语法糖,先将父类实例对象的属性和方法,加到 this 上面,然后再用子类的构造函数修改 this。
class A {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
const xxx = new A(10, 100)
class B extends A {
constructor(length) {
// 如果子类中存在构造函数,则需在使用 this 之前首先调用 super
super(length, length)
}
}
好题分享
Q1:写出下面的答案
function Person() {}
let personl = new Person();
person1.__proto__ = ? // Person.prototype
Person.__proto__ = ? // Function.prototype
Person.prototye.__proto__ = ? // Objet.prototype
Object.__proto__ = ? // Object.prototype
Object.prototype.__proto__ = ? //null
Q2:写出下面的答案
function SuperType(name) {
this.name = name;
}
SuperType.prototype.sayName = function() {
alert(this.name)
}
function SubType(name, age) {
// 通过调用父构造函数的 call 方法来实现继承
SuperType.call(this, name)
this.age = age;
}
SubType.prototype.sayAge = function() {
alert(this.age)
}
var instance1 = new SubType('Nicholas', 29)
instance1.colors.push('black');
instance1.sayName(); // "Nicholas"
instance1.sayAge(); // 29