Kivi

没有什么远大理想,只是永远都不会满足而已


  • 首页

  • 关于

  • 标签

  • 归档

javascript原型

发表于 2016-11-09 更新于 2017-07-02 分类于 javascript 阅读次数:
本文字数: 5.5k 阅读时长 ≈ 5 分钟

关于js原型的思考

一直想讨论js原型的问题,但却不知道怎么入手,后来突然想到,能不能从作者的角度思考呢?

我要什么

  • 我需要面向对象编程
  • 我需要一门简单的的语言,所以不需要传统面向对象编程中类的概念,还要能够实现传统面向对象编程语言中类的功能

类和对象

面向对象编程,最重要的是类和对象,我不想要类,所以最重要的就是对象,所以第一步就是一切皆对象。

虽然没有类,但是我需要实现类的功能啊!没有类怎么生成对象呢?其实由类生成对象的过程,在传统面向对象的语言中,是偏语言底层的过程。我不想搞这么复杂,算了,直接用代码生成对象吧。用代码生成对象,就是执行一段js代码,生成了一个对象。

生成对象的问题解决了,要让生成对象这个代码段规范统一起来,就要和普通的代码段不一样,我得给这个东西规范一个名字,而且代码段最能让人想到的东西就是函数了,就叫这个构造函数吧!那么现在新的问题来了,在这个一切皆对象的世界里,构造函数算什么呢?肯定不能是别的,为了更加直接简单,我让构造函数也是对象,不对是所有的函数都是对象。怎样让函数也是对象?这里先不展开,先贯彻函数也是对象的概念就好。

解决了类和对象的问题,接下来就是面向对象编程的几个重要概念的实现了。

封装和继承

先说抽象和封装吧。抽象不说了,直接封装。为了简单,我让对象就是一个键值对的集合,键不能重复,值任意。现在我需要有个地方封装“属性”和“方法”(在js里,全都是对象,只不过表现形式分为属性和对象),而且我需要静态的属性方法,大家共享,不能修改,节省空间。动态的属性方法,大家各用各的。我没有类,我要怎么办才能封装呢?我需要一个容器,这里就是以键值对的方式装着属性方法。又因为类图通常是树形结构的,(看java,哈哈)所以我也要搞一个属性结构的属性方法容器,每一个节点都是一个容器,我用这个容器去关联对象,被同一个容器关联的所有对象,我称他们是同一个类的实例化对象,这样我就实现了静态属性方法的封装功能。(因为属性方法是共享的,所以只能是静态的),动态的属性方法怎么解决呢?必须在创建对象的时候重新开辟内存空间复制这些属性方法给新创建的对象,上文提到了,创建对象使用构造函数,动态属性方法的分配可以在这个构造函数执行的过程中进行。综上所有,动态的属性方法,封装在构造函数中,静态的属性方法封装在容器中,这个容器就是原型,原型到根节点的最短路线,叫做原型链。

再说说继承的问题,继承需要和封装关联起来。静态属性的继承,只需要让原型链上的任一节点上都能访问到从当前节点到原型链顶点所有属性方法,就是功能上实现了继承。静态属性的继承需要原型链路,动态属性的继承,解决问题的方式就是通过一个构造函数链路实现,子构造函数执行的时候调用父构造函数即可。

这样就理清楚了js中,类,对象,原型,构造函数的一些基本的概念,接下来讨论用这种方式解决面向对象编程的一些细节和问题

对象和函数(Object和Function)

首先要讨论的还是先有鸡还是先有蛋的问题,既然一开始就明确了,js一切皆对象,那么这个设定已经基本确定了就是先有蛋了。
又因为js封装继承的原理都是借助于原型链来实现的,所以还是先从原型链开始,顶端(第一个容器,或者说连容器都算不上),没有任何功能,除了代表原型的起点。真正的js万物的起点,应该是这么一个对象:首先,它是有生成对象的能力的,这就决定了,这个对象一定是一个函数。

现在做这么几个规定:

  • 每个函数关联一个容器,容器封装静态属性方法,为继承做准备。这个容器叫做prototype,prototype实现的效果是,当使用这个函数做为构造函数的时候,生成的对象拥有prototype里面的所有属性方法
  • 每一个对象都有一个__proto__的属性,通过这个属性,可以访问到生成这个对象的构造函数关联的容器,即prototype。(注意即使不通过__proto__也是可以访问到原型上的属性的)

这样的话,就基本解决了封装和继承的问题,那么产生了另外一个问题,函数也是对象,那么函数的__proto__是啥?更要命的是Object的__proto__是啥?Object已经是最接近原型链顶端的对象(函数)了,我们之前又约定__proto__代表了自己的构造函数的prototype属性,那么到底是谁构造了Object了呢?

说到这,又要引入另外一个神奇的东西了,Function—函数的起点。首先,作为具有类能力的对象,Function也是个函数,又因为函数也是对象,所以Function只能由Object拓展而来,即Function是继承了Object的属性方法的,Function.prototype.__proto__ === Object.prototype。但是作为所有函数的起点,bject和Function本身又都是函数,所以Object和Function都是由Function实例化而得,即Object.__proto__ === Function.__proto__ === Function.prototype

上面那段话很绕,但是却基本解决了面向对象编程过程中的细节问题。自此,Object实例化出对象,Function实例化处函数,构建了js世界万象。原型和构造函数给了js面向对象编程的能力。

原型相关概念的理解

Object 和 Function 关系

  • 从原型角度理解

先看下面四个等式:

1
2
3
4
console.log(Object.prototype.__proto__); // null
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true

另外一种获取对象__proto__属性的方法是Object.getPrototypeOf

Object的prototype的原型,就是原型链路的起点,而Function的prototype的原型,是Object的prototype,这说明了Function还是拓展于Object的。
再说说Object.__proto__和Function.__proto__都指向Function.prototype表明了Function是所有函数的基类

用一个图来表示
Object和Function

我参考了这篇博客,写的很详细,值得参考

  • 从拓展方面理解

先看个图
Object和Function

Object和Function分别走了两个方向,Object偏向于做为所有对象的基类,Function偏向于做为函数的基类型

拓展Object原型:

1
2
3
4
Object.prototype.foo = 'bar';
console.log(Function.foo); // bar
console.log(Function.prototype.foo); // bar
console.log(Object.foo); // bar

拓展Object.prototype,就相当于拓展了Function.prototype,又因为Object和Function都由Function拓展而来的,所以当你拓展Function.prototype,那么Function和Object就都被拓展了,所以才有了Function.foo和Object.foo都等于bar

接着上面的例子

1
2
3
4
5
6
var foo = new Object();
function Foo() {

}
console.log(foo.foo); // bar
console.log(Foo.foo); // bar

这说明实例对象和函数都被拓展了

总结:Object拓展了所有的object,Object拓展了Function,Object拓展了自己的属性(通过拓展Function拓展自己)

拓展Function原型:

1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.foo = 'bar';

console.log(Function.foo); // bar
console.log(Function.prototype.foo); // bar
console.log(Object.foo); // bar

var foo = new Object();
function Foo() {

}
console.log(foo.foo); // undefined
console.log(Foo.foo); // bar

以上说明了Function原型拓展只能拓展未被实例化的函数,实例化对象无法拓展

__proto 和 prototype 的理解

所有的对象都有__proto__属性,指向他们构造函数的prototype
当然,所有的函数也都有__proto__属性,指向Function.prototype
只有函数有prototype属性,函数的prototype属性,就是为封装操作提供的,让对象共享属性方法

对于区别:
从实际效果上来说,可以认为__proto__是用来扩展Function的,扩展出来的函数,可以直接调用,不需要new出对象才能用,同时对象是不会扩展通过__proto__扩展的方法或属性的。

  • 扩展__proto__
1
2
3
4
5
6
7
function Foo(){}
Foo.__proto__.test="__proto__ test property found";//通过__proto__扩展
Foo.__proto__.addextend=function(){alert("Foo add extend by __proto__");}
Foo.addextend();//可以执行
var foo=new Foo;
alert("Foo:"+Foo.test);//可以访问
alert(foo.addextend);//未定义

对于prototype来说,它是针对对象的,也就是Function是无法使用的,只有new出来的才能有效

  • 扩展prototype
1
2
3
4
5
6
7
8
function Foo(){}
Foo.prototype.test="prototype test property found";
Foo.prototype.addextend=function(){alert("Foo add extend by prototype");}
alert(Foo.addextend());//未定义
var foo=new Foo;
alert("Foo:"+Foo.test);//无法访问
foo.addextend();//可以执行
alert("Foo instance:"+foo.test);//找到了
  • 通过__proto__扩展Object
1
2
3
4
5
6
7
8
Object.__proto__.test4extend="123";//扩展Object的原型
alert("Function:"+Function.test4extend);//在Function中出现了test4extend属性
alert("Object:"+Object.test4extend);//在Object中出现了test4extend属性,此时Object还是个Function
var obj=new Object;
alert("Object instance:"+obj.test4extend);//未定义
function Foo(){}
var foo = new Foo;
alert("foo object:"+foo.test4extend);//未定义

Function扩展自Object,但是Function对Object又有影响,这是通过Object.__proto__就是(===)Function.prototype建立的联系。记住这个联系后,我们还要记住__proto__和prototype的区别,前者扩展的只可以被Function直接调用,后者扩展的只可以通过其实例调用。另外,还要注意__proto__和prototype的链的概念,这是因为,他们可以互相关联,访问到Function或Ojbect的内容。

原型操作常用操作符总结

  • constructor // 对象有个.constructor 属性,指向自己的构造函数
  • instanceof // instanceof运算符,判断左边的实例是否又右边的构造函数生成
  • isPrototypeOf // 这个方法用来判断,某个proptotype对象和某个实例之间的关系 Cat.prototype.isPrototypeOf(cat1)
  • hasOwnProperty // 每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。
  • in // in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。in运算符还可以用来遍历某个对象的所有属性
# javascript
MongoDB聚合操作小节
冰雪大世界-黑龙江·哈尔滨
  • 文章目录
  • 站点概览
kivi

kivi

nodejs | server
58 日志
17 分类
32 标签
RSS
  1. 1. 关于js原型的思考
    1. 1.1. 我要什么
    2. 1.2. 类和对象
    3. 1.3. 封装和继承
    4. 1.4. 对象和函数(Object和Function)
  2. 2. 原型相关概念的理解
    1. 2.1. Object 和 Function 关系
    2. 2.2. __proto 和 prototype 的理解
    3. 2.3. 原型操作常用操作符总结
© 2019 kivi | 173k | 2:37
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Pisces v7.3.0
|