# 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) // 石兴龙
此时我们可以认为, obj
是 a
的原型。
原型上不仅可以定义属性,还能定义方法。
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