zoukankan      html  css  js  c++  java
  • 前端要给力之:分解对象构造过程new()

    本文讨论JavaScript中的对象创建运算new。需要说明的是,本文所讨论的“将new()过程分解为多个步骤”,并非一般js开发中的所须技巧,而是在js来构建OOP系统的必要技术。

    一、JavaScript构造器与构造过程的特点

    JavaScript中通过以下方式声明和使用构造器:

    其中xxx与yyy的不同在于:对于obj1和obj2来说,yyy是相同的属性的不同引用,不同对象实例的初始值总是相同的;而xxx则是各自不同的引用,每个对象实例都不同。上例中使用1、2这样的值类型数据,并不足以体现二者的区别。当我们而使用引用类型(例如数组),就很容易看出二者的区别来了。例如:

    使用原型的另一个必要在于继承树的建立。这一过程要求new运算符的参与,简单说来,就是"子类.prototype"必须赋值为"new 父类()"所产生的一个实例。例如:

    在这个过程中,最后面的是一行修正代码。这行修正的必要性在于:new MyObject()所产生的实例(例如x)的属性x.constructor总是指向MyObject,而在子类MyObjectEx()中,它应该是指向MyObjectEx的。

    OK。这就是全部的基础知识,下面我们来分解这一过程。

    二、new()过程中的原型链维护

    new总是因为建立原型继承树而存在的,如果没有new过程参与,则当

    时,我们无法通过instanceof运算:

    来了解obj在继承树上的关系。

    但是许多人并不知道,事实上这一过程并不需要MyObject的参与。因为instanceof只检查prototype链,并不检查函数本身。举例来说:

    在这个示例中,obj创建自Y(),但只因为X.prototype(或者它的原型链中)指向原型P,所以最后显示obj也是X的一个实例。可见instanceof的检测与obj是否创建自函数X是无关的,并不检查函数自身。

    换而言之,new()在上述过程中,只有“维护原型链”的作用。那么,我们事实上也可以手工来维护这个原型链。这一点,事实上也就是ECMA Script 5th中的Object.create()出现的原因。

    Object.create(O[, Properties]) returns an object with a prototype of O and properties according to Properties (as if called through Object.defineProperties).

     不考虑Properties的部分,那么Object.create可以用以下过程来描述:

    通过这个create方法,我们可以创建任意的、足够长的原型链,然后把它“赋值给”某个构造器函数,使“原型链创建过程”与“构造器中的初始化过程”二者分解开来。例如:

    这样一来,我们没有显式地声明MyObject()与MyObjectEx()之间的继承关系,但B与C的原型关系维护了它们之间的类属关系。因此:

    三、new()过程中的构造器调用

    对于构造器MyObject()来说,在new()过程中会被调用一次。例如此前提到的:

    new()过程将以刚刚创建的实例为this来调用MyObject(),这使得我们有机会为这个实例(this)做一些初始化操作。这个行为其实来自于最最早期的JavaScript设计。在1995年底发布的Netscapes Navigator 2.0 beta以及其后的NN2.0正式版中,这个最原始版本的JavaScript都还没有“原型继承”的设计,但已经有了new这个运算。这时所谓新对象的创建,就是不断地为this赋值而已,只不过new会为产生的对象维护<obj>.constructor属性。

    这件事是很容易做到的。在高版本中的Function已经提供了Function.call()和Function.apply()方法,能很方便的重现这一过程。因此:

    就可以被分解为如下两个步骤(fixed at 2010.12.30. note: 接受book_LoveLiness的意见,将constructor的改写过程放在原型阶段):

    对于上述{constructor: MyObject},内部隐含了如下两个过程:

    当用户使用自己的构造器来创建MyObject的原型链——例如MyObject()的父类是ExtObject(),而不是Object()的时候,就会用到这样的分解了。

    即使对于最早期没有call()/apply()方法,也没有原型继承的JavaScript,上述过程也可以分解为:

    当然,这一过程也就与原型继承没有了关系——我们假定这就是JavaScript 1.0的时代吧。

    四、构造过程分解的意义

    因为原型继承(包括原型链维护)与构造器调用是可以分开的,所以我们事实上也可以只使用二者之一来创建任何复杂的过程。对于大型OOP框架来说,“是否需要维护原型链”是一个深入的话题。例如一些早期的OOP框架就不管不顾,只考虑子类对象对父类的“相似性”,而无视instanceof运算的效果,这样的时代大概可以追溯到2004年以及之前。后来OOP框架意识JavaScript的原型系统的重要性,因此又回到正途上来,

     - 通过类似于Object.create()的方案,来保证原型链的有效性;又

     - 通过独立的类创建或对象创建过程,来保证系统的可扩展性。

    这一切,就是我们现在的JS OOP的来源了。

    在一些具体的方法中,有许多变形的实现。在QoBean中的Unique()函数综合地重现了整个过程(fixed at 2010.12.30,考虑到无args参数的情况):

    对于任何一个对象A来说,可以用它为原型创建一个新的实例:

    或在创建之后,使用函数MyObject()来初始化:

    或在上述初始化中使用特定的参数:

  • 相关阅读:
    JavaScript
    94.Binary Tree Inorder Traversal
    144.Binary Tree Preorder Traversal
    106.Construct Binary Tree from Inorder and Postorder Traversal
    105.Construct Binary Tree from Preorder and Inorder Traversal
    90.Subsets II
    78.Subsets
    83.Merge Sorted Array
    80.Remove Duplicates from Sorted Array II
    79.Word Search
  • 原文地址:https://www.cnblogs.com/encounter/p/2188577.html
Copyright © 2011-2022 走看看