一、原型
1.1、什么是原型对象
-
原型对象的本质,就是一个普通的Object实例
{}
-
每个函数都有一个
prototype
属性,该属性指向的是原型对象(显示原型对象) -
每个实例对象身上都有一个
__proto__
属性,该属性指向的也是原型对象(隐式原型对象) -
构造函数的显示原型 === 当前构造函数实例对象的隐式原型
function Person(name, age) {
this.name = name;
this.age = age;
this.showAge = function() {
console.log(this.age)
}
}
Person.prototype.showName = function() {
console.log(this.name)
}
var person1 = new Person('WANG', 18);
console.log(person1) // 隐式原型
console.log(Person.prototype) // 显示原型
1.2、原型对象的作用
给原型对象添加属性(一般是方法),这个函数的所有实例对象自动拥有原型中的属性(方法)
1.3、笔试题
function Fn() {
this.test1 = function() {
console.log('test1()')
}
}
Fn.prototype.test2 = function() {
console.log('test2()')
}
var fn = new Fn()
fn.test1()
fn.test2()
console.log(fn.toString())
fn.test3()
结果:
test1() test2() [object Object] Uncaught TypeError: fn.test3 is not a function at test.html:27(报错)
解析:
fn.test1() // 1. 在实例本身找到 fn fn.test2() // 2. 在实例的原型上找到,fn.__proto__ console.log(fn.toString()) // 3. 在实例的 原型 的 原型Object找到 toString方法 fn.__proto__.__proto__ fn.test3() // 4. 都找不到,报错
1.4、图解面试题
二、原型链
2.1、什么是原型链
js查找 对象的属性 过程:
-
先在自身找,如果自身找不到,就沿着
__proto__
找原型对象 -
如果原型对象没有,继续沿着
__proto__
,直到找到Object
的原型对象 -
如果还没有找到,返回
undefined
那么,原型链就是:沿着__proto__
查找的这条链 就是原型链
三、instanceof
- 表达式:
A instanceof B
- 原理:如果 B 的显示原型对象 在 A对象的原型链上,返回true,否则返回 false
- 即
A.(多个__proto__) === B.prototype
function Foo() {}
var f1 = new Foo()
console.log(f1 instanceof Foo) // true, 等同于下面
// console.log(f1.__proto__ === Foo.prototype)
console.log(f1 instanceof Object) // true, 等同于下面
// console.log(f1.__proto__.__proto__ === Object.prototype)
四、完整原型链图解
对应的编程题
console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Object) // true
console.log(Function instanceof Function) // true
function Foo(){}
console.log(Object instanceof Foo) // false
console.log(Foo instanceof Object) // true
五、笔试题
1. 第一道(附解析)
var A = function() {
}
A.prototype.n = 1
var b = new A()
A.prototype = {
n: 2,
m: 3
}
var c = new A()
console.log(b.n, b.m, c.n, c.m)
答案: 1, undefined, 2, 3
解析:
var A = function() { // 1. 一个A的函数对象 } A.prototype.n = 1 // 2. A的原型对象,分配地址例如:0x123, 内容为 {n:1} var b = new A() // 3. A的实例化对象b,__proto__指向地址:0x123,内容为:{n:1} A.prototype = { // 4. A的原型对象改变指针,指向新的地址:0x345,内容为:{n:2, m:3} n: 2, m: 3 } var c = new A() // 5. A的实例化对象c, __proto__指向地址:0x345,内容为:{n: 2, m:3} console.log(b.n, b.m, c.n, c.m)// 6. 那么b内容为{n:1} , c内容为:{n: 2, m:3}
2. 第二道(附解析)
var F = function(){}
Object.prototype.a = function(){
console.log('a()')
}
Function.prototype.b = function(){
console.log('b()')
}
var f = new F()
f.a()
f.b()
F.a()
F.b()
答案:
f.a() // a() f.b() // 报错,下面就不会运行了 F.a() F.b()
解析:
var F = function(){} // 1. 第一个,作为函数对象, 第二个,所有的函数都是 Function 的实例对象 Object.prototype.a = function(){ // 2. Object的原型对象,有:a() console.log('a()') } Function.prototype.b = function(){ // 3. Function的原型对象,有:b() console.log('b()') } var f = new F() // 4. F作为函数对象的实例化对象f // 5. f的原型链上,第一个__proto__是:F的原型对象,没有值 // 第二个 __proto__是:Object的原型对象,有a() f.a() // 所以输出 a() f.b() // 因为没有b,返回undefined,undefined不是函数,所以undefined()报错 // 6. F作为Function的实例对象,第一个__proto__是:Function的原型对象,有 b() // 第二个__proto__是:Object的原型对象,有a() F.a() // 所以输出 a() F.b() // 所以输出b()
所以,将报错注释掉,那么就输出 a()、(报错) 、a()、b()