共计 4158 个字符,预计需要花费 11 分钟才能阅读完成。
JavaScript
中的原型和原型链问题,始终是困扰老手乃至于宽广前端工程师的重要问题,确实,绝对于一般语法来说,它会更加难以了解,在日常开发过程中也不常见。然而……它的重要性是显而易见的。上面咱们就来探索一下。
进入主题
1. 三个重要属性
要了解原型问题,先要理解以下三个属性。
prototype
__protp__
constructor
进入逐个解说阶段:
1.1 prototype
JavaScript
中的每个构造函数都有 prototype
对象。所有实例对象须要共享的属性和办法都放在这个对象里。而那些不须要共享的,就放在构造函数中。应用过程中,不须要咱们手动申明一个 prototype
属性。有了它,咱们就能够将所有须要共享的办法提取到一处,防止冗余。通过一个小栗子来看下
// 定义一个动物的构造函数。function Animal(name) {
// 构造函数中寄存的是不须要共享的办法和属性
this.name = name; // 定义每个动物的名字
}
// 将每个动物须要共享的办法和属性放到一块
Animal.prototype.eat = function () {console.log('吃货色')
}
// 输入本人的名字
Animal.prototype.sayName = function () {console.log(this.name)
}
let dog = new Animal('狗'); // 实例化一个狗的对象
let cat = new Animal('猫'); // 实例化一个猫的对象
dog.sayName(); // 狗
cat.sayName(); // 猫
dog.eat();
cat.eat();
这里,咱们通过 Animal
实例化的 dog
和cat
类,都没有申明 sayName
函数,然而它们都有这个函数能够执行。而实例对象一旦创立,将主动援用 prototype 对象的属性和办法。
置信你曾经 get 到了这个知识点,别急,持续看上面这种状况。
// 再次定义 Animal。function Animal() {}
Animal.prototype.eat = function () {console.log('吃货色')
}
let dog = new Animal();
dog.eat = function () {console.log('我只吃骨头!')
}
dog.eat(); // 我只吃骨头
留神:
上述例子阐明:只有在 dog
和cat
上找不到 eat
办法的时候才会向 Animal
查找,如果能查找到,则不会执行 Animal
的办法。这种状况也叫做 办法的重写。
下面的例子置信你曾经看的很明确了,不明确也问题不大,上面的解说咱们还会通过理论的问题来介绍。接下来看第二个重要的属性。
看的累了就劳动会儿吧,上面的内容更精彩…………
1.2 __proto__
很多人容易将 prototype
与 __proto__
(双下划线) 混同,因为它们之间的指向有点儿绕。不过,一通则百通????,等你真正了解了,就会发现,其实挺简略的。
在很多状况下,__proto__
能够了解为 结构器的原型,这是为什么呢?接着看小栗子吧。
function Animal () {}
let dog = new Animal();
// 重点来了…………
console.log(dog.__proto__ === Animal.prototype) // true
能够看到,dog.__proto__
和 Animal.prototype
是齐全相等的。好了,根本应用看完了,咱们来探索下 __proto__
的指向问题吧。
1.2.1 __proto__
的指向问题
可能大家不太了解,为什么 __proto__
还有指向。这里须要做个阐明,通过不同形式创立进去的对象,它的 __proto__
指向是不同的。
1. 字面量形式创建对象
这个大家比拟容易了解,日常工作中咱们也是应用的比拟多。
let dog = {}
console.log(dog.__proto__) // Object {} --> Object 的原型
// 验证是否相等
console.log(dog.__proto__ === Object.prototype) // true
这里因为间接给 dog
赋值为了全局的对象,所以 __proto__
指向了Object.prototype
????。
2. 结构器形式创立
话不多说,间接上栗子。
function Animal() {}
let dog = new Animal()
console.log(dog.__proto__) // A {} --> A 的原型
// 验证
console.log(dog.__proto__ === Animal.prototype) // true
这是因为 dog
是Animal
的一个实例,所以 dog
的__proto__
指向了Animal.prototype
。
3.Object.create 形式创立
let Animal = {c: 1 // 为了辨别,咱们这里加一个自定义属性。}
let dog = Object.create(Animal)
console.log(dog.__proto__) // Animal {c: 1}
// 验证
console.log(dog.__proto__ === Animal) // true
留神:
Object.create()
办法创立一个新对象,应用现有的对象来提供新创建的对象的 __proto__
。也就是说,这里间接将Animal
作为了 dog
的__proto__
。
1.3 constructor
这个属性不用多说,指的就是构造函数,每个对象都会有一个 constructor
属性来指向本人的构造函数
function Animal() {} // 持续应用这个应用了很屡次的 Animal 构造函数
console.log(Animal === Animal.prototype.constructor) // true
这里的Animal.prototype.constructor
就指向了 Animal
函数。
重点来了????????????????????????????
2.__proto__、prototype 和 constructor
的关系
关系太简单,只能通过图解来阐明了。
<center><img src=”./1.jpeg”></center>
3. 原型链
晓得了三者的概念,理解了它们的用处,接下来想弄懂原型链就会变得简略了。原型链就是无数个上图的串联。咱们还是通过图解的形式来看。
<center><img src=”./5.jpeg”></center>
图太长了,不过千万别被图吓到哟。原型链就是从实例对象开始,通过 __proto__
来向上查找,直到找到 null
为止。
附加项:
看到这里,大家可能会有一个疑难。每个构造函数都有 prototype
对象,那么这个对象到底指向到了哪儿?
其实,每个 prototype
对象的最终指向都是Object.prototype
。为什么这么说,是因为每个构造函数在创立的时候会调配一块儿空间来存储须要共享的办法和属性,所以 prototype
是通过对象创立来的,那么它的最终指向必定也是Object.prototype
。老规矩,咱们来通过栗子来验证一下。
function dog(){}
function cat(){}
function monkey(){}
console.log(dog.prototype.__proto__ === Object.prototype) // true
console.log(cat.prototype.__proto__ === Object.prototype) // true
console.log(monkey.prototype.__proto__ === Object.prototype) // true
4. 面试题了解
4.1 请查看下列程序,会输入什么
function F() {}
Object.prototype.a = function () {}
Function.prototype.b = function () {}
var f = new F()
F.a()
F.b()
f.a()
f.b()
解析:
F.a F.b f.a
执行没问题,因为即便在构造函数总找不到这两个函数,然而通过原型链查找到。
F.a
函数的查找程序是F 构造函数自身 --> Function.prototype --> Function.prototype.__proto__
F.b
函数的查找程序是F 构造函数自身 --> Function.prototype
f.a
函数的查找程序是f 自身 --> f._proto__(Function.prototype) --> f.__proto__._proto__(Object.prototype)
而 f.b
则会呈现执行出错的状况
起因是这样的:
查找程序和 f.a
雷同,然而,在 f.__proto__._proto__
,也就是Object.prototype
上查找不到 b
函数,最终导致查找失败,呈现谬误。
聪慧的你必定学会了,接下来挑战一下吧!!!
function User() {}
User.prototype.hello = function () {}
let u1 = new User()
let u2 = new User()
console.log(u1.hello === u2.hello)
console.log(User.prototype.constructor)
console.log(User.prototype === Function.prototype)
console.log(User.__proto__ === Function.prototype)
console.log(User.prototype === Function.__proto__)
console.log(u1.__proto__ === u2.__proto__)
console.log(u1.__proto__ === User.__proto__)
console.log(Function.__proto__ === Object.__proto__)
console.log(Function.prototype.__proto__ === Object.prototype.__proto__)
console.log(Function.prototype.__proto__ === Object.prototype)