前戏🤩
在传统的OOP中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。
JS的概念都跟传统的概念不一样呀,我们是否能暂且称为 非主流😂
而在各大书籍中,Javascript常常被描述为基于原型的语言,今天我们来康康如何去诠释原型这个概念。
基于原型的语言???
先来瞅瞅概念 😴
基于原型的语言就是每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。
概念果然就是概念,说的似懂非懂的😮
先看个例子
function Leon(){}
console.log(Leon.prototype);
把上面两行代码复制到Chrome的Console中,可以看到:
我定义了个Leon函数,这个函数就自己带了个prototype。所以说Leon的原型对象就是Leon.prototype
注意哦,这是两个对象,如下图所示:
可以看到,定义的function Leon(){}就是构造函数,该函数上的prototype指向原型对象(Leon.prototype),在原型对象中又constructor属性,该属性又指向了构造函数。 好绕啊有没有😫
那我们接着尝试,如果我们在构造函数上加上个属性试试。
Leon.company = 'ns';
可以看到它加在了原型对象(Leon.prototype)指向的构造函数(Leon())上
接着来,我们在Leon的原型对象上继续加上company属性
Leon.prototype.company = 'tencent';
可以看到他将属性加到了与constructor同级的属性上。也就是Leon.prototype中。
好啦,那这阵问题来了,当我输入Leon.company会显示ns还是tencent??
因为Leon.company这句话的意思就是明确表示我要Leon.company,同理,Leon.prototype.company就是原型对象上的company。
来new一下🥾
接下来,我们用JS来新建一个实例。
什么,你还不懂实例??? 好吧我解释一下,
几年前刚开始学Java的时候,第一堂课就说Java是一个面向对象的语言。
对于Java编程来说,首先得先创建一个类,然后实例该对象。
比如,长下面这个样子🎈
说上面的东西就是了解一个概念,什么是类,什么是实例。
上述Java示例中,Animal是一个类,在第十行new出来的一个新的Animal对象复制给a,此时a就是Animal的一个实例。
在Javascript中,一个函数就可以相当于Java中的类了,如果要新建一个实例,它可以是下面这个样子:
function Leon(){}
var l = new Leon()
是不是很简单,此时l称为实例对象,实例对象都有一个私有属性**(称之为_proto_)**
接着说 function Leon(){}称为构造函数,该函数指向构造函数上的原型对象**(Leon.prototype)**
记住两点:①__proto__和constructor属性是对象所独有的;② prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。
Object(包含Functoin, Array, Date, RegExg, Error)这些里面都有prototype
我们验证一下上面两句话,分别打印l实例对象和Leon.prototype原型对象
可以看到确实存在!😏
然后我们分别加上属性
function Leon(){}
Leon.prototype.foo = 'bar'
var l = new Leon()
l.color = 'red'
可以看到foo属性添加到了原型对象上(Leon.prototype),color属性添加到了l实例对象上。
来康康MDN怎么说的
JavaScript 只有一种结构:对象。每个实例对象( object )都有一个私有属性(称之为 _proto_ )指向它的构造函数的原型对象(prototype )。
是不是有点明白了
qio duo ma die,等下,我们再看一遍那个图
红框的地方是不是长的一模一样,莫非👽,他们是一个??来验证下吧
l.__proto__ === Leon.prototype
果真,就是一个📢
然后我们新建一个对象
var abc = {}
然后去看看这个
然后我们发现他有__proto__属性
之后我们把abc.__prototype__与Leon.prototype属性相等。
可以看到,Leon构造函数的内容已经全部复制到了abc中
是不是已经实现了new的操作啦!!!
更细节的new我放到了后面啦
来说原型链啦🌹
既然它们是一个,说明就是通过原型对象进行连接的。
l._proto_ 指向 Leon.prototype
而原型对象(Leon.prototype)本身就是一个对象,本着是对象就有原型对象的定义🤷♀️,所以它也有一个_proto_
然后,我们把__proto__显示出来康康
可以看到
红框的地方就是原型对象(Leon.prototype)的__proto__属性,它的上级可以在constructor中看到是Object.
这也就解释了function在Js中也是Object的一种。
然后我们来说说,浏览器是怎么进行属性查找的。
当我们访问l.color,浏览器会查l中是否有这个属性,如果有,则返回。
如果没有,则会在l.__proto__中查找,如果有,则返回,没有,则在l._proto_._proto_(也就是Leon.prototype._proto_)中查看是否有该属性,如果有,则返回,
如果没有,则在l._proto_._proto_.__proto__上查找,当原型链上所有的__proto__都查找完了,都没找到,则返回undefined。
以上操作像不像一个链条呀,所以称为原型链 (禁止套娃🙅)
MDN上这么说:
原型对象也有一个自己的原型对象( _proto_ ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
现在一步一步将如下代码复制到Console中
function Leon(){}
Leon.prototype.bar = 'bar in LeonPrototype'
Leon.bar = 'bar in Leon'
Leon.prototype
Leon.bar
Leon.prototype.bar
然后new一个新对象
var a = new Leon()
a.bar
a.bar = 'cc'
a.bar
delete a.bar
a.bar
delete a.__prototype__.bar
a.bar
如果我在Object中加入原型呢
a.__proto__.__proto__.bar = 'bar in ObjectPrototype'
可以看到bar挂到了Object的原型上
所以此时
a.bar
实现一个new😛
实现一个new有四步
- 1.创建空对象(即{})
- 2.链接该新创建的对象(即设置对象的__proto__)到该函数的原型对象prototype上
- 3.将步骤1新创建的对象为this的上下文
- 4.如果该函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),则返回新创建的对象
上面我们已经实现第1,3步了,来康康其他两步。🤞
关于Arr.prototype.slice的解释developer.mozilla.org/zh-CN/docs/…
function leonNew(ctor){
//这句话的解释参见下文链接
var args = Array.prototype.slice.call(arguments, 1);
//1.创建空对象(即{})
var obj = {};
//2.链接该新创建的对象(即设置对象的__proto__)到该函数的原型对象prototype上
obj.__prototype = ctor.prototype;
//3.将步骤1新创建的对象为this的上下文
var result = ctor.apply(obj,args);
//4.如果该函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),则返回新创建的对象
var isObject = typeof result === 'object' && result != null;
var isFunction = typeof result === 'function';
return isObject || isFunction ? result : obj;
}
关于[].slice.call(arguments, 1)的意义
参考链接:blog.csdn.net/suners88267…