大方無隅,大器晚成,大音希聲,大象無形。——《道德經》javascript
首先要了解几个属性constructor
、prototype
、[[prototype]]
、__proto__
分别做用是什么,还要理解几个概念原型、原型链、构造函数。函数
结合代码先把上面的的属性和记录清楚。post
constrcutor
是一种用于建立和初始化class
建立的对象的特殊方法。 构造函数
自己就是一个函数,与普通函数没有
任何区别,不过为了规范通常将其首字母
大写。构造函数
和普通函数
的区别在于,使用 new
生成实例的函数就是构造函数
,直接调用的就是普通函数
。下面示例代码:性能
function ConstructorFun (name) {
this.name = name;
}
// 经过new关键字建立实例
let constructorfun = new ConstructorFun();
复制代码
其实ConstructorFun
就是一个普通函数,可是在经过new
关键字生成实例的时候,就能够把这个函数叫作构造函数;ui
除了null
、undefined
其余不管是经过new
生成的实例,仍是经过字面量生成的变量,普通的函数都是有constructor
属性的。this
代码以下:spa
function ConstructorFun (name) {
this.name = name;
}
// 经过new关键字建立实例
var constructorfun = new ConstructorFun();
constructorfun.constructor === ConstructorFun; // true
var number = 111;
console.log(number.constructor); // ƒ Number() { [native code] }
复制代码
let a = {}
实际上是 let a = new Object()
的语法糖let a = []
实际上是 let a = new Array()
的语法糖function Foo()
{ … } 实际上是 var Foo = new Function(…)instanceof
判断一个函数是否为一个变量的构造函数手动实现一个instanceof
函数以下:
// 模拟实现instanceof
function selfInstanceof (left, right) { //left 表示左表达式,right 表示右表达式
let cur = left.__proto__; // 取的cur的隐式原型
let parent = right.prototype; // 取的right的显式原型
while(true) {
if (cur === null) { // 若是cur为null 直接返回false
return false;
}
if (cur === parent) { // 若是cur与parent相同 返回true
return true;
}
cur = cur.__proto__; // 上面两个条件都不知足,继续向上一层原型链查找
}
}
复制代码
对于引用类型来讲constructor
属性值是能够修改的,可是对于基本类型来讲是只读的。
注意:
null
和undefined
是没有constructor
属性的。
官方解释原型:"JavaScript常被描述为一种基于原型的语言(prototype-based language)————每一个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。" 每一个函数都有一个特殊的属性就叫做原型(prototype)
,请看下面代码:
function Foo () {}
console.log(Foo.prototype);
复制代码
效果以下图所示:
Foo.prototype
上有两个属性,一个是
constructor
它指向了函数自己;另外一个是
__proto__
它指向了
Object.prototype
。
构造函数Foo
有一个指向原型的指针,原型Foo.prototype
有一个指向构造函数的指针Foo.prototype.constructor
,用下面的图来表示更清晰一点:
其实更重要的是任何一个prototype
对象都有一个constructor
属性,指向这个构造函数。
在上面看到__proto__
这个属性,每一个实例对象(object)都有一个隐式原型
属性(称之为__proto__
)指向了建立该对象的构造函数的原型
。
function Foo (name) { this.name = name; }
var foo = new Foo();
复制代码
效果图以下:
new Foo()
生成的实例对象
foo
,它有一个
__proto__
属性指向
Foo.prototype
,能够经过如下代码验证:
foo.__proto__ === Foo.prototype; // true
复制代码
Foo
、Foo.prototype
、Foo.prototype.constructor
、foo.__proto__
三者的关系以下图所示:
__proto__
属性在 ES6 时才被标准化,以确保 Web 浏览器的兼容性,可是不推荐使用,除了标准化的缘由以外还有性能问题。为了更好的支持,推荐使用
Object.getPrototypeOf()
。
经过改变一个对象的
[[Prototype]]
属性来改变和继承属性会对性能形成很是严重的影响,而且性能消耗的时间也不是简单的花费在obj.__proto__ = ...
语句上, 它还会影响到全部继承自该[[Prototype]]
的对象,若是你关心性能,你就不该该修改一个对象的[[Prototype]]
。
若是要读取或修改对象的 [[Prototype]]
属性,建议使用以下方案,可是此时设置对象的 [[Prototype]]
依旧是一个缓慢的操做,若是性能是一个问题,就要避免这种操做。
// 获取
Object.getPrototypeOf()
Reflect.getPrototypeOf()
// 修改
Object.setPrototypeOf()
Reflect.setPrototypeOf()
复制代码
若是要建立一个新对象,同时继承另外一个对象的 [[Prototype]]
,推荐使用 Object.create()
。
function Parent() {
age: 50
};
var p = new Parent();
var child = Object.create(p);
复制代码
[[Prototype]]
是对象的一个内部属性,外部代码没法直接访问。
遵循 ECMAScript 标准,
someObject.[[Prototype]]
符号用于指向 someObject 的原型
每一个对象拥有一个原型对象,经过 __proto__
指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null
。这种关系被称为原型链 (prototype chain)
,经过原型链一个对象会拥有定义在其余对象中的属性和方法。看一面一张经典的图可能更直观:
看一下面的代码:
function Foo () {}
var foo = new Foo();
foo.__proto__ === Foo.prototype; // true
foo.__proto__.__proto__ === Object.prototype; // true
foo.__proto__.__proto__.__proto__ === null; // true
复制代码
下面的图能够很好的展现上面的代码prototype
和__proto__
指向问题。
Symbol
是基础数据类型,它能够经过Symbol(123)
生成实例,不能经过new Symbol()
生成实例,Symbol
不是构造函数,可是它有constructor
属性。
let sSymbol = Symbol('symbol');
let errSymbol = new Symbol('symbol'); // Uncaught TypeError: Symbol is not a constructor
Symbol.constructor; // ƒ Symbol() { [native code] }
复制代码
prototype
属性,指向函数对象的原型,原型对象上有一个constructor
属性指向构造函数自己。constructor
属性值是能够修改的,可是对于基本类型来讲是只读的,固然 null
和 undefined
没有 constructor
属性。__proto__
属性在 ES6
时被标准化,但由于性能问题并不推荐使用,推荐使用 Object.getPrototypeOf()
。__proto__
是每一个实例上都有的属性,prototype
是构造函数的属性,在实例上并不存在,因此这两个并不同,但 foo.__proto__
和 Foo.prototype
指向同一个对象。__proto__
指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null
,这就是原型链。