原型相关
每个函数都有 prototype 属性,除了 Function.prototype.bind() 该属性指向原型。
每个对象都有 __proto__ 属性,指向了该对象的构造函数的原型。其实这个属性指向了[[prototype]],但是[[prototype]]是内部属性,我们并不能访问到,所以使用 __proto__来访问
对象可以通过 __proto__ 来寻找不属于该对象的属性,__proto__将对象连接起来组成了原型链
小结
- Object 是所有对象的爸爸,所有对象都可以通过 proto 找到它
- Function 是所有函数的爸爸,所有函数都可以通过 proto 找到它
- Function.prototype 和 Object.prototype 是两个特殊的对象,他们由引擎来创建
- 除了以上两个特殊对象,其他对象都是通过构造器 new 出来的
- 函数的 prototype 是一个对象,也就是原型
- 对象的 proto 指向原型, proto 将对象和原型连接起来组成了原型链
new
- 新生成一个对象
- 链接到原型
- 绑定this
- 返回新对象
在调用 new 的过程中发生以上四件事,下面是自己实现的new
js
function myNew() {
// 创建一个空对象
let obj = Object.create(null)
// 获得构造函数
let con = [].shift.call(arguments)
// 链接到原型
obj.__proto__ = con.prototype
// 绑定this,执行构造函数
let result = con.apply(obj, arguments)
// 确保new出来的是个对象
return typeof result === 'object' ? result : obj
}对于实例对象来说,都是通过new产生. 对于创建一个对象来说,更推荐使用字面量方式创建(无论是性能还是可读性),因为使用 new Object() 方式创建对象通过作用域链一层层找到Object,但使用字面量方式没这个问题。
instanceof
可正确判断对象的类型,因为内部机制是通过判断对象的原型链中是不是找到类型的prototype
继承
在ES5中,我们可以使用如下方式解决继承问题:
js
function Super() {}
Super.prototype.getNo = function() {return 1}
function Sub() {}
let s = new Sub()
Sub.prototype = Object.create(Super.prototype, {
constructor: {
value: Sub,
enumerable: false,
writable: true,
configurable: true
}
})以上继承的实现思路就是将子类的原型设置为父类的原型
在ES6中,我们可以通过class语法轻松解决这个问题
js
class MyDate extends Date {
test() {
return this.getTime()
}
}
let myDate = new MyDate()
myDate.test()