一、什么是原型链
MDN英文解释:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
JavaScript is a bit confusing for developers experienced in class-based languages (like Java or C++), as it is dynamic and does not provide a class
implementation per se (the **class**
keyword is introduced in ES2015, but is syntactical sugar, JavaScript remains prototype-based).
When it comes to inheritance, JavaScript only has one construct: objects. Each object has a private property which holds a link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with **null**
as its prototype. By definition, **null**
has no prototype, and acts as the final link in this prototype chain.
Nearly all objects in JavaScript are instances of [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)
which sits on the top of a prototype chain.
个人理解:实例有自身[[prototype]]属性,指向构造函数的prototype,构造函数原型对象也有自己的[[prototype]],这样通过[[prototype]]串联起来的继承关系,叫作原型链,原型链的顶端是Object.prototype(Object.prototype.[[prototype]] 指向 null)
mdn中的代码示例:
// Let's create an object o from function f with its own properties a and b:
let f = function () {
this.a = 1;
this.b = 2;
}
let o = new f(); // {a: 1, b: 2}
// add properties in f function's prototype
f.prototype.b = 3;
f.prototype.c = 4;
// do not set the prototype f.prototype = {b:3,c:4}; this will break the prototype chain
// o.[[Prototype]] has properties b and c.
// o.[[Prototype]].[[Prototype]] is Object.prototype.
// Finally, o.[[Prototype]].[[Prototype]].[[Prototype]] is null.
// This is the end of the prototype chain, as null,
// by definition, has no [[Prototype]].
// Thus, the full prototype chain looks like:
// {a: 1, b: 2} ---> {b: 3, c: 4} ---> Object.prototype ---> null
console.log(o.a); // 1
// Is there an 'a' own property on o? Yes, and its value is 1.
console.log(o.b); // 2
// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited.
// This is called Property Shadowing
console.log(o.c); // 4
// Is there a 'c' own property on o? No, check its prototype.
// Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4.
console.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? No, check its prototype.
// o.[[Prototype]].[[Prototype]] is Object.prototype and there is no 'd' property by default, check its prototype.
// o.[[Prototype]].[[Prototype]].[[Prototype]] is null, stop searching,
// no property found, return undefined.
二、函数Object与函数Function的关系
Object[[prototype]]指向构造函数Function.prototype
Function[[prototype]]指向Function.prototype
Function.prototype[[prototype]]指向Object.prototype
Object.prototype为原型链顶端,其原型指向null,包含了一个toString,valueOf方法,很多对象的valueOf和toString继承于此,有一个例外是Array.prototype.toString方法,该方法重写了(将每个元素字符串后,用’,’连接)。
三、后代实例可以操作原型的引用类属性,原始类值属性则不行。
难点:
如果示例视图操作原型上的原始值类型,可能会给自身添加属性
四、this
(1)对象中,当函数作为对象里的方法被调用时,this
被设置为调用该函数的对象。
this 的绑定只受最接近的成员引用的影响
let obj = {
name: 'obj',
o : {
fn : function(){console.log(this.name)},
nane: 'o'
}
}
obj.o.fn() // => 'o'
(2)原型链中,如果该方法存在于原型链中,this为调用该方法的实例
(3)构造函数中,使用new关键字,this将被绑定到实例(this被return出去)
(4)作为dom事件处理函数,this指向触发事件的元素
(5)在class中,方法和普通函数一样,取决去caller,想要改写这个行为,可在cunstructor中,绑定到this上。
class Car{
constructor(){
this.sayHi = this.sayHi.bind(this)
}
sayHi(){
console.log(this.name)
}
get name(){
return 'Car name'
}
}
五、创建对象的方法:
(1)字面量 var obj = {}
(2)new构造函数 var obj = new Object()
这两种方式一样,字面量方式,赋值更方便,隐式通过new Object来创建。原型都是Object.prototype
(3)通过自定义构造函数创造的对象,原型指向构造函数的prototype
(4)通过Object.create()参数为实例对象的原型,可以改变js默认指定的原型,用来自定义实例原型,继承特定对象,如果参数传null,则该实例没有原型,所以并非所有对象都继承于Object.prototype。这样的实例是不能手动添加proto的,手动添加上的是自定义属性。对象原型是系统添加的。对象属性可以更改,但是不能手动添加
let obj = {}
let obj1 = Object.create(Object.prototype)
console.log(obj.__proto__ === Object.prototype) // => true
六、包装类原型函数方法调用
let num = 1
console.log(num.toString())
// => '1' 并非调用Object.prototype上的toString方法,调用的是Number.prototype.toString
// 为什么Number会自己写个toString方法呢?
// Object.prototype.toString.call(1) 返回的是'[object Number]'
// Number.prototype.toString.call(1) 返回的是'1'
七、call和apply
作用:调用方法,并改变其内部this指向
方法调用其实有隐式地call
apply后一个参数为数组,传参更方便一些,用得频率更高
function test(){}
test() // => test.call() 系统有隐式地调用call,没传参
function Car (name,age){
this.name = name
this.age = age
}
var newCar = {}
Car.call(newCar,'benz','one')
console.log(newCar) => {name: 'benz',age:'one'}
借用构造函数
call和apply的使用指导,将不同的构造函数结合起来,丰富实例的属性与方法。适合协同开发、方法分类、在原有功能上拓展。
function Computor1 (){
this.plus = function(a,b){console.log(a + b)}
this.minus = function(a,b){console.log(a - b)}
}
function Computor2 (){
//使用apply,给丰富实例属性方法
Computor1.apply(this)
this.mul = function(a,b){console.log(a*b)}
this.divide = fcuntion(a,b){console.log(a%b)}
}
let fullComputor = new Computor2()