原型继承
- JavaScript 中,所有的对象都有一个隐藏的
[[Prototype]]
属性,它可以是另一个对象或者null
。 - 我们可以使用
obj.__proto__
进行访问(还有其他方法,但很快就会被覆盖)。 [[Prototype]]
引用的对象称为“原型”。- 如果我们想要读取
obj
属性或者调用一个方法,而且它不存在,那么 JavaScript 就会尝试在原型中查找它。写/删除直接在对象上进行操作,它们不使用原型(除非属性实际上是一个 setter)。 - 如果我们调用
obj.method()
,而且method
是从原型中获取的,this
仍然会引用obj
。因此方法重视与当前对象一起工作,即使它们是继承的。
函数原型
当用 new F()
创建一个新对象时,该对象的 [[Prototype]]
被设置为 F.prototype
prototype只出现在构造函数上
obj.__proto__ === Object.prototype
原生的原型
所有的内置原型顶端都是 Object.prototype
。有些时候人们说“一切都从对象上继承而来”。
- 所有的内置对象都遵循一样的模式:
- 方法都存储在原型对象上(
Array.prototype
、Object.prototype
、Date.prototype
等)。 - 对象本身只存储数据(数组元素、对象属性、日期)。
- 方法都存储在原型对象上(
- 基本数据类型同样在包装对象的原型上存储方法:
Number.prototype
、String.prototype
和Boolean.prototype
。只有undefined
和null
没有包装对象。 - 内置对象的原型可以被修改或者被新的方法填充。但是这样做是不被推荐的。只有当添加一个还没有被 JavaScript 引擎支持的新方法的时候才可能允许这样做。
模拟new
new操作符做了这些事:
- 它创建了一个全新的对象
- 它会被执行[[Prototype]](也就是proto)链接
- 它使this指向新创建的对象
- 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上
- 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用
- 新生成了一个对象
- 链接到原型
- 绑定 this
- 返回新对象
// objectFactory(constructor, 'cxk', '18')
function objectFactory() {
// 创建一个空的对象
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
}
模拟Object.create
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
// 模拟 Object.create
function create(proto) {
function F() {}
F.prototype = proto;
return new F();
}
实现instanceOf
instanceof
可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype
。
// 模拟 instanceof
function instance_of(L, R) {
//L 表示左表达式,R 表示右表达式
var O = R.prototype; // 取 R 的显示原型
L = L.__proto__; // 取 L 的隐式原型
while (true) {
if (L === null) return false;
if (O === L)
// 这里重点:当 O 严格等于 L 时,返回 true
return true;
L = L.__proto__;
}
}
实现一个call
- 将函数设为对象的属性
- 执行&删除这个函数
- 指定this到函数并传入给定参数执行函数
- 如果不传入参数,默认指向为 window
Function.prototype.myCall = function (context) {
const context = context || window
// 给 context 添加一个属性
// getValue.call(a, 'yck', '24') => a._func = getValue
context._func = this
// 将 context 后面的参数取出来
const args = [...arguments].slice(1)
// getValue.call(a, 'yck', '24') => a._func('yck', '24')
const result = context._func(...args)
// 删除 _func
delete context._func
return result;
}