~$ 存档

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

今天周末,夜里没事再把JS梳理一下。有兴趣的可以静下来当成个故事慢慢看。

首先说的对象。比如 var o={}
创建一个对象o,但是我们没有看到构造函数。根据面向对象的思路,此处应该有个构造函数。比如C++的new 类型。

我们应该猜到,这是语法省略了。可经过这么省略,很多事情就变的迷惑了。事实上,完整的写法是:var o = new Object()

其中,Object()就是一个内置的构造函数。这一步非常关键,是下面一些理论的基础

 

在书上,我们常常看到原型(prototype)的说法,原型是函数才能拥有的,即原型是函数的原型。就像上面的Object(),它是一个函数,因此
它具有原型。但又因为它是内置的函数,因此赋予了很多方法。查看:Object.prototype。

Object.prototype

对象o因为通过Object()构造而来,因此它能引用Object的原型(prototype),在浏览器中可以通过o.__proto__来查看

因此,可以总结出下面一段话:

原型prototype是函数才具有的,表现形式为:函数.prototype
由此函数创建的对象可以引用原型,表现形式为:对象.__proto__,换句话说,对象要引用构造的原型,需要给它一个属性来引用,这个引用是__proto__

 

现在,我们再来看下函数,相对来说,它是JS中比较复杂的一种类型。但是在这里,我们首先也把它看成对象。原因是:在JS中一切皆对象

在学习过程中,需要把思路简化,进行最高抽象,我们记不住那么多的理论,但是最核心的理论需要记住

1、在js中一共有七种数据类型(不考虑ES6,分别为数值布尔字符串函数对象undefinednull

除了undefined和null(这两者暂时不考虑),甚下的都理解为对象,包括函数。

那么这种抽象的结果是什么呢?就是任何'对象'都可以使用 类型.__proto__,来查看它的原型链

2、函数非常特殊,首先(只有)它有prototype,又因为它是对象,所以又有__proto__,所以它具有两项本领,是极特殊的一个

 

现在,我们认真分析下函数

比如:创建一个函数

function f(){}

上面说函数是一个对象,那我们查看一下它的__proto__,发现得到的是native code。在这个地方,我们解决一个问题。

比如,问f.call的call方法从来哪来的?我们现在在脑子中可以把对象划分成两种,一种是[普通对象],一种是[函数],原因是函数非常特殊。

一、假如是普通对象,我们问的上一级一定是个函数,因为对象是由[new 构造函数]得来的。
二、假如是函数,因为它也是对象,我们也会问它的出处

下面分别解释

假如是普通对象,我们问它的__proto__,它指向的一定是某个函数的prototype。例如:

let a=10
a.__proto__ //此处等于Number.prototype

let b='hello'
b.__proto__ //此处等于String.prototype

...
let o ={}
o.__proto__ //此处等于Object.prototype

let f=function(){}
let ob=new f()
ob.__proto__ //此等于f.prototype

换句话说,查看一个对象的__proto__就是往上级查找.

好了,现在我们要查看一下ob的某个属性,比如ob.show()方法。查找顺序如下:
首先,ob对象本身有吗?如果没有,我们到它的__proto__里面看看有没有,又因为ob.__proto__=f.prototype,所以就来到f.prototype。但是,我们知道,一般情况下f.prototype至少会挂两个数据:constructor和__proto__

f.prototype={
  constructor:..,
  __proto__:..
  xxx可能的其它属性:...
}

发现里面也没有,那么我们再一次根据prototype里面的__proto__向上查找,为什么呢?因为prototype也是一个对象,完整的话是这样说的:下面的人从我要一个属性,我没有,但又因为我自身也是一个对象,于是我就拿出自己的__proto__查看下有没有?

就来到Object.prototype里面,继续查找,依然没有发现,查找结束。这就是原型链查找模式。

第二,刚才我们说的是普通意义的'对象',下面看下函数

函数也是对象,这个说法已经从最开始到现在重复了很多遍,所以要加深记忆。好了,现在我要查找f的某个属性,比如f.show()方法,首先就看它本身有没有?比如下面代码:

function f(){
    let a=10
}
f.show=function(){
    console.log('show方法')
}

这种写法没有任何异议,因为函数也是对象,挂载几个属性没有问题。假如把f.show()去掉,再来调用f.show(),查找顺序如下:

1、f本身有吗?发现没有,进入2
2、因为它是对象,就查看它的__proto__
3、进入f.__proto__,发现它是native code,一步就终止。

所以,像f.call()这种调用就是native code赋给它的本领,因为从字面上无法找到它的踪迹

那么,如果给f.prototype赋予一个show()方法呢?可惜的是,任何对象的原型跟踪只从它的__proto__进行,所以没用,代码如下:

function f(){}
f.prototype.show=function(){
    console.log('show')
}
f.show() //可惜未定义,只能从__proto__那条线上查找

 

下面进行总结

查找一个对象的属性

1、首先看它本身有没有

2、如果没有,就在它的__proto__中看看有没有

3、如果没有,又因为__proto__(做为对象)里面肯定又会包含__proto__,再进入这个__proto__里面看看有没有

4、以此向上类推...一直追踪到源头

 

如果是一个函数对象,那么向上查找一步就结束,因为函数对象的__proto__是native code

结论的启示:

如果想赋予一个对象的某个属性,只有两个办法:

1、自身赋值,比如
let o={}
o.show=function(){}

2、在它上一级的prototype挂载。如果不想在上一级挂,就在上一级的上一级挂载,这种方式就是继承。比如:

let f=function(){}
f.prototpye.show=function(){}
let o=new f()
o.__proto__=f.prototype //返回true
o.show()  //等价于 o.show() = o.__proto__.show() = f.prototype.show()

 

下面做一个综合的例子

<script>
    function A(){
    }
    A.prototype.show=function(){
        console.log('来源于函数A的show')
        }

    function B(){}
    B.prototype=new A()

    let o=new B()
    o.show()
</script>

 对于这个查找属性的例子,只需要两个最终的结论:

1、对象自身有没有
2、如果没有,就在它的__proto__中查找,这个__proto__一定还有__proto__,以此向上类推.. 原因是:对象一定有__proto__。我们会发现,结论越来越短,也越来越明了

【分析如下】

1、首先看对象o是否有show,没有得到,就在它的__proto__中查找,o.__proto__ = B.prototype
2、由于B.prototpye = new A(),可知是一个普通对象。在这里特别说明的是,__proto__和prototype都是普通对象。之所以
   在一些似乎非常简单的问题上重复多次,是为了深刻加深印象,把一些概念训练成本能,一次一次又一次的重复
3、我们给new A()起个别名,比如对象aa。那么就在aa中查找是否有show()方法,发现没有
4、于是,问题变成在对象aa中查找show()方法。由于它不具有,就在aa.__proto__中查找,可知 aa.__proto__ = A.prototype
5、于是,又来到A.prototype中查找,发现了show()方法,查找结束

假如函数A自身就具有show()方法,那么就不会在它的prototype中查找,代码如下:

 

function A(){
    this.show=function(){
        console.log('来源于对象A的方法show') //对象的方法
    }
}

A.prototype.show=function(){
    console.log('来源于A原型上的show')
}

function B(){}
B.prototype=new A()

let o=new B()
o.show()

 

经过这些分析,相信对于__proto__和prototype已经分辨清楚了

 

posted on 2020-09-20 21:10  LuoTian  阅读(160)  评论(0编辑  收藏  举报