• JavaScript 的原型对象 Prototype


    在 JavaScript 中,每当定义一个对象(或函数)时候,对象中都会包含一些预定义的属性,其中一个属性就是原型对象 prototype

    var myObject = function( name ) {
    	this.name = name;
    	return this;
    };
     
    console.log(typeof myObject.prototype); // object
     
    myObject.prototype.getName = function() {
    	return this.name;
    };
    

    上面的代码创建了一个函数,然后赋值给 myObject。如果我名调用 myObject(),它将返回window 对象。因为它是在全局作用域内定义的,而且它还没有被实例化,所以 this 直接指向全局对象:

    console.log(myObject() === window); // true
    

    原型链

    JavaScript 中定义或实例化任何一个对象的时候,它都会被附加一个名为 __proto__ 的隐藏属性,原型链正是依靠这个属性才得以形成。但是千万别直接访问 __proto__ 属性,因为有些浏览器并不支持直接访问它。另外 __proto__ 和 对象的 prototype 属性也不是一回事,它们各自有各自的用途。

    怎么理解呢?其实,当我们创建 myObject 函数时,实际上是创建了一个 Function 类型的对象:

    console.log(typeof myObject); // function
    

    这里要说明一下,Function 是 JavaScript 中预定义的一个对象,所以它也有自己预定义的属性(如length 和 arguments)和方法(如 call 和 apply),当然也有 __proto__,以此实现原型链。也就是说,JavaScript 引擎内可能有类似如下的代码片段:

    Function.prototype = {
    	arguments: null,
    	length: 0,
    	call: function() {
    		// secret code
    	},
    	apply: function(){
    		// secret code
    	},
        ...
    };
    

    事实上,JavaScript 引擎代码不可能这样简单,这里只是描述一下原型链是如何工作的。

    我们定义了一个函数 myObject,它还有一个参数 name,但是并没有给它任何其它属性,例如length 或者其它方法,如 call。那么下面这段代码为啥能正常执行呢?

    console.log(myObject.length); // 结果:1,是参数的个数
    

    这是因为我们定义 myObject 时,同时也给它定义了一个 __proto__ 属性,并赋值为Function.prototype(参考前面的代码片段),所以我们能够像访问其它属性一样访问myObject.length,即使我们并没有定义这个属性,因为它会顺着 __proto__ 原型链往上去找length,最终在 Function 里面找到了。

    那为什么找到的 length 属性的值是 1,而不是 0 呢,是什么时候给它赋值的呢?由于 myObject是 Function 的一个实例:

    console.log(myObject instanceof Function); // true
    console.log(myObject === Function); // false
    

    当实例化一个对象的时候,对象的 __proto__ 属性会被赋值为其构造者的原型对象,在本示例中就是 Function,此时构造器回去计算参数的个数,改变 length 的值。

    console.log(myObject.__proto__ === Function.prototype); // true
    

    而当我们用 new 关键字创建一个新的实例时,新对象的 __proto__ 将会被赋值为myObject.prototype,因为现在的构造函数为 myObject,而非 Function

    var myInstance = new myObject('foo');
    console.log(myInstance.__proto__ === myObject.prototype); // true
    

    新对象除了能访问 Function.prototype 中继承下来的 call 和 apply 外,还能访问从 myObject 中继承下来的 getName 方法:

    console.log(myInstance.getName()); // foo
     
    var mySecondInstance = new myObject('bar');
     
    console.log(mySecondInstance.getName()); // bar
    console.log(myInstance.getName()); // foo
    

    其实这相当于把原型对象当做一个蓝本,然后可以根据这个蓝本创建 N 个新的对象。

    prototype 的典型示例

    用过 jQuery 或者 Prototype 库的朋友可能知道,这些库中通常都会有 trim 这个方法。

    示例:

    String.prototype.trim = function() {
    	return this.replace(/^s+|s+$/g, '');
    };
    

    trim 用法:

    ' foo bar   '.trim(); // 'foo bar'
    

    但是这样做又有一个缺点,因为比较新版本的浏览器中的 JavaScript 引擎在 String 对象中本身就提供了 trim 方法, 那么我们自己定义的 trim 就会覆写它自带的 trim。其实,我们在定义 trim 方法之前,可以做个简单的检测,看是否需要自己添加这个方法:

    if(!String.prototype.trim) {
    	String.prototype.trim = function() {
    		return this.replace(/^s+|s+$/g, '');
    	};
    }
    

    检查一下,如不存在 trim 这个方法,定义一个。

    via http://net.tutsplus.com/tutorials/javascript-ajax/prototypes-in-javascript-what-you-need-to-know/

  • 相关阅读:
    传输问题
    修改对象目录
    传输与系统单点登录问题
    两个小错误
    BW数据库优化过程记录20100529
    SAP ABAP 到外部数据库Oracle问题
    固定资产传输问题
    软件外包的商业模式和软件人员的职业规划
    做有意义的事,继续研究FarMap
    FarMap诞生了!
  • 原文地址:https://www.cnblogs.com/chengjun/p/5234906.html
Copyright © 2020-2023  润新知