原型
概念:
原型(prototype)是函数上的一个属性,它定义了构造函数制造的对象的公共祖先,它是一个对象,包含了可以由该构造函数创建的所有实例共享的属性和方法。
Person.prototype.lastName = 'Park'
Person.prototype.say = function () {
console.log('hello');
}
function Person(a, b) {
this.name = 'Peter'
}
let p1 = new Person() //隐式具有 lastName say
console.log(p1.lastName);
p1.say()
输出的lastName 是有值得,say也被定义了
构造函数new出来的对象会 隐式继承到 构造函数原型上的属性
例子: 用车的数据举例:
如果我们每一位用户购买同一型号的车,那除了一些可自定义的变量,其他都是固定的,像车长和车高,那么我们这里就可以将这些固定的数据设置为它们共同“祖先”。
Car.prototype.name = 'su7'
Car.prototype.lang = 5000
Car.prototype.height = 1400
function Car(color, owner) {
// this.name = 'su7'//免去给每一个不同的对象付相同的值
// this.lang = 5000
// this.height = 1400
this.color = color
this.owner = owner
}
运行截图:
就算没有在Car里给name,long,height赋值,但是由于它的prototype中有定义,所以还是能输出值。
1. 实例对象可以修改显示继承的属性,但无法修改隐式继承的属性(原型上的)
还是车的例子,我们加上这行代码:
this.name = 'su9'//把车名改为su9
我们再运行:
看到结果真的是su9,但是要注意的是,这个并不是不prototype里的name改成了su9, 而是在Car中强加了一个属性name,为su9,prototype里的name我们是访问不到的。
所以
实例对象可以修改显示继承的属性,但无法修改隐式继承的属性(原型上的)
2.实例对象无法给原型新增属性
要给原型加属性的话只能采用这种办法直接往原型上加:
Car.prototype.name2 = 'su77'
3.实例对象无法删除原型上的属性
跟增加属性一样,这里我们采用 delete 和 直接在原型上进行删除 两种方法
delete tian.long
console.log(tian.long)
delete Car.prototype.long
console.log(tian.long)//undefined
可以看到和增加的结果一样,采用delete进行删除的话long还是有值的,害得直接在原型上删。
constructor
constructor 属性是 prototype 对象的一部分
constructor是一个特殊的属性,它存在于每个对象的原型链上,指向创建该对象的函数
看例子:
function Car() {}
let car = new Car()
console.log(car.constructor);//记录该对象是由谁创建的
- 尽管
constructor属性非常有用,但它不是只读的,可以被修改。也就是说可以手动设置或更改constructor属性
我们加上代码:
function Bus() { }
Car.prototype = {
constructor: Bus
}
- 实例对象里的constructor是从原型里继承来的
function Person() {}
let p = new Person()
console.log(Person.prototype); //函数原型 显示原型
console.log(p.constructor);
对象原型
这个就是谷歌浏览器上的对象原型:
以前的写法是
car.__proto__
对象的隐式原型 === 创建它的构造函数的显示原型
function Person() {
}
let p = new Person()
console.log(Person.prototype); //显示原型(构造函数的原型)
console.log(p.__proto__); //p.__proto__ 隐式原型(实例对象的原型)
我们看下面的运行截图就能理解了,这两个运行出来的结果一模一样:
js引擎在查找属性时,会先查找对象显示具有的属性,找不到,再查找对象的隐式原型(proto)
简要概述:
- 查找对象的自有属性:当访问一个对象的属性时,JavaScript引擎首先会检查这个属性是否存在于对象自身的属性列表中。
- 原型链上查找:如果对象自身没有这个属性,JavaScript引擎会沿着原型链向上查找,即检查对象的
_proto_属性(在某些JavaScript实现中,这可能表现为Object.getPrototypeOf(obj))所指向的原型对象。 - 继续向上查找:如果原型对象也没有这个属性,引擎会继续沿着原型链向上查找,直到找到属性或者到达原型链的末端(通常是
null)。 - 属性未找到:如果在原型链的末端也没有找到属性,那么属性访问将返回
undefined。
原型链
js引擎在查找属性时,会顺着对象的隐式原型向上查找,找不到,则查找隐式原型的隐式原型,一直向上,直到找到null为止,这种查找关系,称之为原型链
看例子:
GrandFather.prototype.say = function () {
console.log('hahaha');
}
function GrandFather() {
this.age = 60
this.like = 'drink'
}
Father.prototype = new GrandFather()
function Father() {
this.age = 45
this.fortune = {
card: 'visa'
}
}
Son.prototype = new Father()//{age:40,fortune:{xxx}}
function Son() {
this.age = 18
}
let son = new Son()
我们运行输出console.log(son.say()),得到
也就是说son在father中没有找到say方法,去了grandfather里找,也没有,再去grandfather的原型上找,找到了say(),输出hahaha。如果原型链上都没有,那它会一直这么找,直到找到object的隐式原型null。
所有对象都有原型?
直接说结论,不是的, Object.create(null)没有原型
Object.create(XXX)的作用是创建一个新对象,让新对象隐式继承对象的属性
let a = {
name: 'Tom'
}
let obj = Object.create(a) //创建一个新对象,让新对象隐式继承a对象的属性
console.log(obj)
了解了该方法的使用后我们就能知道,用Object.create(null)创建的对象没有原型,因为它们被明确地指定了原型为null。