# OOP:原型链

# 什么是原型

js 的每一个对象,都有一个 __proto__ 属性

var arr = [1,2,3];
arr.__proto__ === Array.prototype // true

我们在访问一个对象上 不存在的属性或方法 时,js引擎会自动的去 对象的 __proto__ 上去找。

var obj = { name: '石兴龙' }
var a = {}
console.log(a.name) // undefined
a.__proto__ = obj
console.log(a.name) // 石兴龙

此时我们可以认为, obja 的原型。

原型上不仅可以定义属性,还能定义方法。

var obj = {
  name: '石兴龙',
  sayHi: function (params) {
    console.log(`Hi, 我叫 ${this.name}`)
  }
}
var a = {}
console.log(a.name) // undefined
a.__proto__ = obj
a.sayHi()           // Hi, 我叫 石兴龙

# 什么是原型链

通过上面那个例子,我们可以得出一个结论:

要调用自己没有的东西,就去问 __proto__ 要。那么 __proto__ 也没有问谁要呢?

答案是 __proto____proto__

var obj = { name: '石兴龙' }
var obj_2 = { age: 18 }
var a = {}
a.__proto__ = obj
console.log(a.age)  // undefined
obj.__proto__ = obj_2
console.log(a.age)  // 18

上面这段代码定义了3个变量,obj , obj_2 , a。我们看到 a 在调用 a.age 的时候第一次发生了 undefined。 那么此时的查找过程是这样的

  a --> obj // obj并没有 age 属性所以 undefined

之后又给 obj 设置了原型为 obj_2,那么再调用一次 a.age 现在的查找过程是这样的。

  a --> obj --> obj_2 // 刚好 obj_2 有 age 属性。原型查找终止

原型链就是一个不断的 __proto__.__proto__.__proto__.... 的过程。

# new 之后都发生了什么?

上篇文章中构建对象是用 new Student() 这种方法。new 是 js 的关键字。new 都做了哪些工作呢?

javascript 有一个很重要的概念 克隆

javascript 中所有的对象都是从 另一个 对象克隆而来的。new 这个动作只不过是创建了一个 干净的对象 然后 return 给你。我用代码给你演示一下看。

假设有这样一段代码

var Func = function() {

}
var f = new Func();

new 之后都发生了 4 件事

1. 创建对象

let obj = new Object();

2. 设置 obj 原型链 为构造函数的 prototype

obj.__proto__ = Func.prototype

3. 把构造函数的 this 指向 obj, 并执行函数

let result = Func.call(obj);

4. 判断构造函数的返回类型 如果是值类型(string,number,null,undefined,Boolean),则返回 obj ,如果是引用类型则返回 引用类型放弃 obj。

if (typeof result === 'object') {
  return result
} else {
  return obj;
}

构造函数默认的情况下都是返回 undefined,但如果你显式的返回了一个值类型。那么 js 引擎会忽略这个返回值。但如果你返回的是一个 json 或者是其他的引用类型,那么 js 引擎会放弃构造好的 obj 变量。但这样做是无意义的。

还有一点,js中所有的对象都是从 Object.prototype 上克隆而来的。

通过 Object.create(null) 可以创建一个没有原型的对象

var a = Object.create({})
console.log(a.__proto__) // {}
a = Object.create(null)
console.log(a.__proto__) // undefined
上次更新: 9/9/2020, 7:25:14 PM