浅聊一下
JavaScript 中的原型是理解该语言核心特性的关键之一。在面试中,掌握原型概念通常是一个重要的议题。今天我们将深入探讨 JavaScript 中的原型,并且文章末尾附带一道你绝对想不到的网易面试题…
什么是原型?
我们先来聊聊到底什么是原型
原型(prototype)是函数天生就具有的属性,它定义了构造
函数制造出的对象的公共祖先。
通过该构造函数产生的对象,可以隐式继承到原型上的属性和方法
你说这到底是啥呀?语文不好,看不懂…那我们直接上代码
function Person() {
this.name = '张三';
this.age = 18;
}
let p = new Person();
console.log(p);
在js中,我们用这种方法定义一个类,然后new一下创造一个对象,代码较为简单,来看打印结果
我们可以看到,所创建的对象p继承到了Person( )中的属性,并且可以打印出来看到,那么我们再来上一段代码
Person.prototype.say = function () {
return '我是张三'
}
console.log(p.say());
console.log(p);
这次我们调用p.say()
在这里我们可以看到p.say()确实被调用了,说明p身上确实有这个方法,但是打印p的时候却没有显示出来,于是乎,我们可以称function Person()是p的显示原型,而Person.prototype是p的隐式原型
为什么需要原型?
因为可以提取公有属性,简化代码执行
你说这话说的这么简陋,我还是看不懂…话又不多说,直接上代码
Car.prototype.name = 'BMW';
Car.prototype.lang = 4900;
Car.prototype.height = 1400;
function Car(owner,color){
this.owner = owner;
this.color = color;
}
var car = new Car('L','black');
var car2 = new Car('Y','pink');
这里有一个“别摸我”牌汽车的构造函数,Car构造函数的原型上我们定义了公共属性,而函数里有有颜色和拥有者,这些是需要“私人定制”的,所以在new一个对象的时候,相同的属性就不会再重复创建,直接访问隐式原型就可以访问到。在这里“L”和“Y”分别购买了一辆“别摸我”,并且分别定制了颜色…
细节
- 实例对象是无法修改原型的属性和方法的
Car.prototype.name = 'BMW';
Car.prototype.lang = 4900;
Car.prototype.height = 1400;
function Car(owner,color){
this.owner = owner;
this.color = color;
}
var car = new Car('L','black');
var car2 = new Car('Y','pink');
car.name = '劳斯莱斯'
console.log(car);
console.log(car2.name);
聪明的掘友来看看这里输出什么?
我们本意上是想修改他们公共属性上的name,我让car.name=“劳斯莱斯”,但是将他们打印出来以后,我们发现car2的名字任然没有改变,就像“别摸我”公司卖给我一辆汽车,我叫其中的一辆为大黄,但是你并不能改变他的品牌名字叫“别摸我”吧…
那么我们怎么修改他们公共的名字呢?让公司改名呗!
Car.prototype.name = '大黄';
console.log(car);
console.log(car2.name);
太狠了,把公司名都改了…现在叫大黄
- 实例对象是无法删除原型的属性和方法的
既然个人是无法改变品牌的名字的,那么我个人要把你品牌的名字删了,那你能忍?
Car.prototype.name = 'BMW';
Car.prototype.lang = 4900;
Car.prototype.height = 1400;
function Car(owner,color){
this.owner = owner;
this.color = color;
}
var car = new Car('L','black');
var car2 = new Car('Y','pink');
car.name = '劳斯莱斯'
Car.prototype.name = '大黄';
delete car.name
console.log(car);
console.log(car2.name);
不用猜,你只能剥夺你自己车的名字,而car和car2的品牌名依旧叫大黄
要删除品牌名,同样得让公司自己改呀!
Car.prototype.name = 'BMW';
Car.prototype.lang = 4900;
Car.prototype.height = 1400;
function Car(owner,color){
this.owner = owner;
this.color = color;
}
var car = new Car('L','black');
var car2 = new Car('Y','pink');
car.name = '劳斯莱斯'
Car.prototype.name = '大黄';
// delete car.name
delete Car.prototype.name
console.log(car.name);
console.log(car2.name);
由于你自己叫自己的车为劳斯莱斯,所以car的名字还是有的
原型链
原型链是什么呢?
顺着对象的隐式原型不断向上查找上一级的隐式原型,直到找到目标或者一直到null,
这种查找关系称为原型链
举一个例子,你把别人的“别摸我”撞坏了,他找你(你的显示原型)赔钱,你拿不出,找你老婆(你的隐式原型),他就找到你爸爸(你爸爸的显示原型),你爸爸拿不出就找到你妈妈(你爸爸的隐式原型),你妈妈也拿不出,于是找你爷爷(爷爷的显示原型),又找不到,还找你奶奶(爷爷的隐式原型),再往上没人了,于是只能认栽…
原型链也是这样,小二,上个代码
<script>
Ground.prototype.lastName = "wang";
function Ground(){
}
var ground = new Ground();
Father.prototype = ground;
function Father(){
this.name = "zhangsan";
}
var father = new Father();
Son.prototype = father;
function Son(){
this.hobbit = "play";
}
var son = new Son();
console.log(son.lastName+son.name);
</script>
让我们逐步分析:
- 首先,定义了一个名为
Ground
的构造函数,然后给Ground.prototype
对象添加了一个lastName
属性,其值为 “wang”。 - 接着,定义了一个
Father
构造函数,其中通过this.name
将name
属性设置为 “zhangsan”。 - 然后,创建了一个
ground
对象实例,它的原型是Ground.prototype
。由于Ground.prototype
上有lastName
属性,所以ground
实例也能访问到lastName
属性。 - 接下来,将
Father.prototype
设置为ground
对象,这样Father
构造函数的实例将会继承ground
对象的属性和方法。 - 定义了一个
Son
构造函数,其中通过this.hobbit
将hobbit
属性设置为 “play”。 - 最后,创建了一个
son
对象实例,它的原型是Father.prototype
,因此它继承了Father
构造函数中的name
属性,同时也能通过原型链访问lastName
属性。因此,console.log(son.lastName + son.name)
会输出 “wangzhangsan”。
你想不到的网易面试题
所有的对象最终都会继承自 Object.prototype?
乍一看,好像没什么不对劲,可这个“所有”两个字又让人望而生畏,那么答案到底是什么呢?
错!大错特错
为什么?,因为我们还有一种创建对象的方法Object.create()
<script>
//Object.create(obj)
let obj = {
a:1
}
let obj2 = Object.create(obj)
</script>
来看看打印obj2的结果
在这里我们可以看到obj2继承了obj,那么我们再来看
let obj3 = Object.create(null)
再来打印obj3
我们可以看到并没有报错,obj3里面什么也没有,并没有继承到Object
这是为什么呢?这里涉及到一个美丽的错误,我们的null用typeof判断出来的类型可是Object啊
至于为什么,大家可以去我以前的文章看看(“探秘JavaScript:类型判断的奇妙世界(一)” – 掘金 (juejin.cn))
结尾
原型看完这篇基本上就搞定了,舒舒服服上床睡觉等待过年,嘿嘿嘿……
原文链接:https://juejin.cn/post/7332708703399673897 作者:滚去睡觉