前言
首先对象都有原型和原型链,通过原型链我们可以找到对象的"继承",可以通过原型链找到父类以及顶级 Object 的参数和方法,并且使用它们。在对象中又包含一个隐式原型(__proto__)和一个显式原型(prototype),构造函数通过 prototype 可以找到它们的原型,而实例函数可以通过 __proto__ 找到它们的原型。
创建对象
创建对象一般有三种方法,字面量、构造函数、Object.create
// 第一种:字面量
var o1 = { name: 'o1' }
var o2 = new Object({ name: 'o2' })
// 第二种:构造函数
var M = function(name) { this.name = name }
var o3 = new M('o3')
// 第三种:Object.create
var P = { name: 'p' }
var o4 = Object.create(P)
构造函数和普通函数的区别
- 如果一个函数被 new 实例后,那么这个函数就是构造函数
- 一般构造函数的函数名首字母都要大写
- 构造函数中都会有自己的 this,而普通函数中的 this 是指向 window 的
构造函数、实例函数、原型对象、原型链
名词的解释
- 构造函数:创建一个函数并且该函数被 new 实例化过
- 实例函数:通过 new 实例化一个函数
- 原型对象:构造函数的原型,可以在上面新增一些属性和方法
- 原型链:实例可以通过
__proto__拿到构造函数的原型
它们之间的关联
这里的核心人物就是构造函数,它可以使用 prototype 获得它的原型,而原型可以通过 constructor 指回构造函数,也可以使用 new 创建一个实例,实例可以通过 __proto__ 获取构造函数的原型,而该构造函数的原型对象你可以看作是父类函数的一个 实例函数
关系如下图:
instanceof的原理
instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,我们知道 typeof 是判断数据类型的,但是它判断不了 Object 类型的继承,所以我们可以使用 instanceof 进行判断,判断某个实例对象是否在对象的原型链上。
但是 instanceof 也有不严谨的地方,它无法分辨判断出来的结果是否是它本身的构造函数,所以想要更精准的判断可以使用 实例对象.__proto__.constructor === 构造函数
new 运算符
原理
- 一个新对象被创建,它继承自 foo.prototype
- 构造函数 foo 被执行:执行的时候,相应的传参会被传入,同时上下文(this)会被指定为这个新实例。new foo 等同于 new foo(),只能用在不传递任何参数的情况
- 如果构造函数返回了一个"对象",那么这个对象会取代整个 new 出来的结果,如果构造函数没有返回对象,那么 new 出来的结果为步骤1创建的对象
模拟 new 运算符
使用 Object.create 设置原型对象
// 模拟一个 new 运算符
function imitationNew(fn) {
let o = Object.create(fn.prototype);
let k = fn.call(o);
console.log(k)
if(typeof k === 'object') {
return k;
} else {
return o;
}
}
function fn() {
this.name = 'oyzx';
this.sayName = function() {
return this.name;
}
}
var a = imitationNew(fn)
console.log(a)
var b = new fn();
console.log(b)
console.log(a.__proto__ === b.__proto__); // true