持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情
1、原型
prototype
在js中每一个函数都有一个属性,prototype
,这个属性是一个对象,在这个对象上,默认会有一个属性constructor
,constructor是一个函数,这个函数指回与之关联的构造函数。
proto
在自定义一个函数时,原型对象只会默认获得constructor
属性,其他的方法都继承自Object。而每次使用构造函数创造一个新的对象时,这个对象就会默认获得一个属性__proto__
,而这个__proto__指向的就是构造函数的prototype。
function Person(name) {
this.name = name;
}
let ly = new Person('ly')
console.log(ly.__proto__)
console.log(ly.__proto__ === Person.prototype) //true
例子
比如我们日常使用字面量定义一个对象,其实底层调用的还是使用的new Object()
来构造的对象。所以我们可以发现我们定义的对象都有一个属性__proto__
,而且它和Object
构造函数的prototype
也是相等的。
又比如,我们使用的布尔值、字符串、Number等在调用其方法时,底层都默认将其转换成了调用他们的构造函数生成的对象。
所以我们才可以使用原始值上面的方法(方法只有对象才有)。
查看原型的方法
Object.getPrototypeOf()
这个方法其实和使用__proto__
查看差不多,都会返回该对象的原型对象。
function Person(name) {
this.name = name
}
let ly = new Person('ly')
console.log(Object.getPrototypeOf(ly) === Person.prototype) // true
设置原型的方法
Object.setPrototypeOf()
let bid = {
num:2
}
let person = {
name:'ly'
}
Object.setPrototypeOf(person,bid)
console.log(person.name) // ly
console.log(person.num) // 2
console.log(Object.getPrototypeOf(person) === bid) // true
原型的作用
在使用构造函数时,如果我们在构造函数上面创建了一个公共的方法,那么每一次new这个构造函数生成对象的时候,都会给生成的对象上面开创一个内存用于存放这个公共的方法。如果我们使用了这个构造函数创建了很多对象,那么就会浪费空间,所以我们可以把公共的方法或者属性放在构造函数的原型(prototype)上面。
function User(name) {
this.name = name;
this.show = function() {
console.log(this.name);
}
}
let p1 = new User('ly');
let p2 = new User('jack');
p1.show(); // ly
p2.show(); //jack
console.log(p1);
console.log(p2);
function User(name) {
this.name = name;
}
User.prototype.show = function() {
console.log(this.name)
}
let p1 = new User('ly');
let p2 = new User('jack');
p1.show(); // ly
p2.show(); //jack
console.log(p1);
console.log(p2);
2、原型链
有时候,在使用构造函数创建一个新的对象的时候,我们发现我们生成的对象上面除了有构造函数原型上面的属性以外,还有其他的属性可以供我们使用,但是当打印生成的对象时,我们却又发现,该对象上面的属性又没有这些方法,那么这些方法是从哪里的来的呢?
——————其实这些方法都是这个函数的原型对象从Object
的原型对象上继承来的。这就是原型链。
function Person(name) {
this.name = name
}
let user = new Person('ly')
user.hasOwnProperty('name') //true
注:[[Prototype]]就是__proto__
因为prototype
是对象嘛,那么该对象是Object
构造函数创建的,而且对象就一定有__proto__
属性,所以prototype
的__proto__
属性指向的就是Object
构造函数的prototype
。
我们可以打印看一下他们是否相等:
function Person(name) {
this.name = name
}
let user = new Person('ly')
console.log(user.__proto__.__proto__ === Object.prototype) // true
那么我们又可以猜想一下,Object
是构造函数,但也是对象,那么这个Object
是不是也就应该同时有__proto__
和prototype
属性呢?答案是对的。
Object
的__proto__
指向的应该就是生成该对象的构造函数的原型对象,而构造Object对象的构造函数又是谁呢?他就是Function
构造函数。我们所有的函数都是使用new Function()
创造的(底层上)。所以Object的__proto__
就应该等于Function的prototype
:
而Function
身上的prototype
又是对象,我们之前也说了对象都是使用的Object
构造函数生成的,所以Function.prototype.__proto__
就应该等于Object.prototype
了:
再回到之前的,Object
也是对象,该对象除了__proto__
也有prototype
属性,这个属性的是对象,那么他的__proto__
又指向谁呢? -----答案是它指向的null
,也就是原型链的顶层。
同时,Function
也是既是函数也是对象,那么它是不是也是由Function
创建的呢?答案是对的,Function.__proto__指向的就是Function.prototype:
是不是晕了,哈哈哈!
总结
1、在js中,原型链的顶层是null
2、在js中,万物皆是对象,任何对象都是由构造函数创建
3、在js中,所有的函数都是由Function
构造函数创建
4、在js中,所有不是由构造函数创建的对象,默认都是由Object
构造函数创建
5、Object和Function都又是构造函数、又是对象。所以Object.__proto__指向Function.prototype, Function.prototype.proto 指向 Object.prototype
6、Function的__proto__指向的是自己的prototype
7、Object.prototype.null指向的是原型链顶层,null
其实我感觉只要把Function和Object这里的原型链看懂了,js的原型链就应该懂了,因为难的地方其实就在这里。
原型链的顺序
在我们使用一个对象的方法或者属性时,如果这个对象本身没有这个属性或者方法,那么,js就会顺着这个原型链往上找,如果找到了,就返回这个属性或者方法,如果顺着原型链到了原型链的顶层,就会返回null。
原型链的改变
其实原型链是可以被改变的,那就是使用继承,将本来一个对象的__proto__指向Object.prototype改为指向其他的对象了,但是改变后,原理还是不变的。这个就下次再说吧。