先熟悉概念,对于JS原型与原型链 有个大致了解:
原型:
JS是一种通过原型实现继承的语言与别的高级语言是有区别的,像JAVA,C#是通过类型决定继承关系的,JS是动态的弱类型语言,总之,可以认为JS中所有都是对象,在JS中,原型也是一个对象,通过原型可以实现对象的属性继承,JS的对象中都包含了一个prototype内部属性,这个属性所对应的就是该对象的原型。prototype作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome内核的JS引擎中提供了proto这个非标准的访问器(ECMA新标准中引入了标准对象原型访问器Object.getPrototype(object))。原型的主要作用就是为了实现继承与访问对象。
原型链:
当我们去访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,于是就这样一直找下去,也就是说我们平时所说的原型链的概念。
一、原型
- 函数的prototype属性
- 每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象)
- 原型对象中有一个属性constructor,它指向函数对象。
- 给原型对象添加属性(一般都是方法)
作用:函数的所有实例对象都自动拥有原型中的属性(方法)。 - 代码示例:
// 每个函数都有一个prototype属性,它默认指向一个Object空对象。(也就是原型对象)
console.log(Date.prototype, typeof Date.prototype);
function Fun() {}
console.log(Fun.prototype); // 默认指向一个Object空对象(没有我们的属性)
// 原型对象中有一个属性construtor,它指向函数对象
console.log(Date.prototype.constructor === Date);
console.log(Fun.prototype.constructor === Fun);
// 给原型对象添加属性(一般是方法) ===> 实例对象可以访问
Fun.prototype.ok = function() {
console.log('ok');
}
var fun = new Fun()
fun.ok()
二、显式原型与隐式原型
- 每个函数function都有一个prototype,即显式原型(属性)
- 每个实例对象都有一个__proto__,可称为隐式原型(属性)
- 对象的隐式原型为其对应构造函数的显式原型的值
- 内存结构:
- 总结:
- 函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象。
- 对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
- 程序员可以直接操作显示原型,但不能操作隐式原型(ES6之前)
- 代码示例:
//定义构造函数
function Fn() {
// 内部默认执行语句: this.prototype = {}
}
// 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象
console.log(Fn.prototype)
// 2. 每个实例对象都有一个__proto__,可称为隐式原型
//创建实例对象
var fn = new Fn() // 内部默认执行语句: this.__proto__ = Fn.prototype
console.log(fn.__proto__)
// 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype===fn.__proto__) // true
//给原型添加方法
Fn.prototype.test = function () {
console.log('test()')
}
//通过实例调用原型的方法
fn.test()
三、原型链
①原型链
- 原型链
- 访问对象的一个属性时,
- 先在自身属性中查找,找到返回
- 如果没有,再沿着__proto__这条链向上查找,找到返回。
- 如果最终没找到,返回undefined。
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()
- 别名:隐式原型链
- 作用:查找对象的属性(方法)
②构造函数、原型、实例对象的关系(图解)
- var o1 = new Object();
var o2 = {};
- function Foo(){ }
- ps: 因为所有函数是Function函数对象的实例对象,所以所有的函数的[proto](隐式原型属性) 都是一样的,都指向 Function 的原型对象。
③ 属性问题
- 读取对象的属性值时:会自动到原型链中查找
- 设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值。
- 方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
- 代码示例:
function Fn() { }
Fn.prototype.a = 'xxx'
var fn1 = new Fn()
console.log(fn1.a, fn1) //xxx Fn{}
var fn2 = new Fn()
fn2.a = 'yyy'
console.log(fn1.a, fn2.a, fn2) //xxx yyy Fn{a: "yyy"}
function Person(name,) {
this.name = name
this.age = age
}
Person.prototype.setName = function (name) {
this.name = name
}
var p1 = new Person('Tom', 12)
p1.setName('Bob')
console.log(p1) //Person {name: "Bob", age: 12}
var p2 = new Person('Jack', 12)
p2.setName('Cat')
console.log(p2) //Person {name: "Cat", age: 12}
console.log(p1.__proto__===p2.__proto__) // true -->所以方法一般定义在原型中
四、instanceof
- instanceof 是如何判断的?
- 表达式:A instanceof B
- 如果B函数的显式原型对象在A对象的原型链上,返回true,否则返回false
- Function是通过new 自己产生的实例
/*
案例1
*/
function Foo() { }
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true
/*
案例2
*/
console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true
function Foo() {}
console.log(Object instanceof Foo) // false
五、相关面试题
测试题1:
/*
测试题1
*/
function A () {}
A.prototype.n = 1
let b = new A()
A.prototype = { n: 2, m: 3}
let c = new A()
console.log(b.n, b.m, c.n, c.m) // 1 undefined 2 3
测试题2:
/*
测试题2
*/
function F (){}
Object.prototype.a = function(){
console.log('a()')
}
Function.prototype.b = function(){
console.log('b()')
}
let f = new F()
f.a() //a()
f.b() //f.b is not a function -->找不到
F.a() //a()
F.b() //b()
console.log(f)
console.log(Object.prototype)
console.log(Function.prototype)