前言
“函数在JavaScript中是一等公民”。为什么这么说,因为函数身上承担着重任,他同时扮演着函数、对象和类的角色。我们都知道,在ES6之前js中是不存在类的,那么如何创建对象呢?我们可以通过在function前面添加new来创建实例。
函数与对象
在js中函数和对象默认都分别拥有一个属性。
- 函数:每个函数有一个属性prototype(以示区分:把他叫做原型对象)
- 对象:每个对象有一个属性_proto_(以示区分:把他叫做对象原型)
既然有着这么独特的属性,背后一定有着很重要的原因,只是我们暂且不说。我们先来看一个示例。
一个示例
我们创建一个空对象,然后直接调用toString()和toArray()方法,结果发现:toString()可以正常打印,而toArray()显示方法不存在。
let obj = {}
console.log(obj.toString()); // [object Object]
console.log(obj.toArray()); // obj.toArray is not a function
这是为什么呢?我们直接打印一下obj这个对象。
原来这个空对象上有个__proto__属性,这个属性上又有个toString()方法。
原型和原型链
我们先来看一张说到原型链就会出现的图
对象xiaoming是由函数Person创建出来的实例,那么xiaoming的_proto_
和Person的prototype属性指向同一个对象。这个原型对象本身也是一个对象,因此它也有__proto__属性,同时它还有一个constructor属性,指向Person。这种不断向上查找__proto__属性的特点就是原型链。
我们通过代码直观地来感受一下。
function Person() {
}
var xiaoming = new Person()
console.log(xiaoming.__proto__ === Person.prototype); // -> true
console.log(Person.prototype.constructor === Person); // -> true
原型和原型链的作用
上面说到js没有类的概念,那它如何实现创建的实例拥有函数的属性和方法呢?这就是靠原型和原型链实现。
js内部有这样的机制:一个对象如果访问某个属性或者方法,就会先去自身查找,如果没找到就去__proto__属性上查找,如果还是没找到,就去继续向上查找_proto_._proto_,直到到达原型链的顶端,如果还是没找到就会显示没找到。
因此要想实现创建出的实例拥有同样的属性或者方法,只需要给原型对象设置属性和方法就可以了。
Person.prototype.sex = 'men'
Person.prototype.eat = function() {
console.log("I can eat!")
}
习题
最后我们来看一个小习题来巩固一下以上所学的知识。这里需要了解一个方法Object.create,我简单介绍一下,如果var a = Object.create(B) ,那么a._proto_ === B。
function Person() {
this.name = "ren"
}
var xiaoming = new Person()
function Student() {
this.age = '12'
}
Student.prototype = Object.create(Person.prototype)
var stu = new Student()
Person.prototype.sex = 'men'
console.log(Student.prototype.__proto__ === Person.prototype);
console.log(stu.__proto__ === Student.prototype);
console.log(stu.__proto__.__proto__ === Person.prototype);
console.log(xiaoming.name);
console.log(stu.name);
console.log(stu.sex);