zoukankan      html  css  js  c++  java
  • 扩展javascript原生对象-prototype原型链彻底掌握

    原文地址: http://javascript.info/tutorial/native-prototypes

    prototype 原型是什么?

    和传统语言对象创建有些不同,比如java,php都是通过new一个class类来创建对应的对象的。

    而javascript则提供了利用prototype来新建对象的机制。也就是从实体对象创建新的实体对象的方法

    从prototype创建新对象的两种方法Object.create, new ClassConstructorFunction()

    以及

    原生的javascript 对象在prototypes里面保存他们可用的方法。

    比如,当我们创建一个新的对象时, 它并未包含任何东西,它怎么知道能够有个toString方法可供使用呢?

       var obj={};
       alert(obj.toString())

    那是因为obj={}是下面语句

    obj = new Object()

    的缩写,而Object是原生的javascript 构造函数,而当我们使用new Object()来创建一个新的对象时,有一个重要的特性就是:产出对象的__proto__指针将指向构造函数的prototype对象,这里就是Object.prototype对象。也就是说obj.__proto__ == Object.prototype,而Object.prototype又是一个包含toString方法的原生对象

    。注意: __proto__是随浏览器的,有可能命名并不相同

    而根据javascript的原型链查找特性,所有在这个链上的属性和方法都是可读的(注意当写一个在原型链上的属性时,会自动创建一个ownProperty,而不会去更改原型上的属性值!!),也正因为此,所以toString方法自然可用了。

    上面的故事同样适用于Array,Function和其他对象。他们的方法分别在Function.prototype, Array.prototype上面定义的、

    再看以下原型链的图(来自网易云课堂)

    当一个对象的属性被访问或者方法被调用时,javascript引擎通过以下算法和顺序来检索:

    1. 在对象自身上面找;

    2. 如果1.找不到,就到对象的__proto__上面找;

    3. 如果2.找不到,就到__proto__.__proto__上面找

    上面这个原型链从对象本身开始持续查找,直到属性被找到(注意这时this为子对象,而非原型),或者直到下一个__proto__ == null(也就是到链底)了而返回undefined. 唯一一个其__proto__属性为 null的对象是 Object.prototype,所有的链最终都指向Object.prototype,换句话说,所有的对象都从Object继承

    更改原生prototypes添加新的方法

    原生的prototypes是可以被修改的,比如我们可以创建新的方法到原型对象上去。

    我们可以写一个方法来多次重复一个字符串

    String.prototype.repeat = function(times) {
      return new Array(times+1).join(this)
    }
    
    alert( "a".repeat(3) ) // aaa

    再例如,我们可以在Object.prototype对象上添加一个each(func)方法,以便我们在每一个属性上来应用func函数。

    Object.prototype.each = function(f) {
      for(var prop in this) {
    if (Object.prototype.hasOwnProperty(prop)) continue // filter
        var value = this[prop]
        f.call(value, prop, value) 
      }
    }
    
    // Now correct
    var obj = { name: 'John', age: 25 }
    obj.each(function(prop, val) {  
      alert(prop) // name -> age 
    })

    通过上面的方法修改原生对象从一开始就招致激烈的评论,有人说不安全,暴露太多到global空间可能造成命名污染,有人说很好用。但无论如何,它在做兼容低版本浏览器的js功能时,用处广泛:

    比如,如果没有Object.create支持,而我们又必须兼容,那么一个可行的方法是:

    if (!Object.create) {
      Object.create = function(proto) {
        function F() {}
        F.prototype = proto
        return new F
      }
    }

    从原生对象继承

    原生对象可以用来被继承,比如,Array.prototype为new Array()创建的对象保持了所有的方法

    如果我们希望这些方法可在我们的myArray中被访问,那么myArray.__proto__ == Array.prototype就有用武之地了。

    // constructor
    function MyArray() { 
      this.stringify = function() {
        return this.join(', ')
      }
    }
    // Make sure that (new MyArray) has the right __proto__ 
    MyArray.prototype = Array.prototype
    
    // Test
    var myArr = new MyArray()
    myArr.push(1)
    myArr.push(2)
    alert( myArr.stringify() ) // 1, 2
    alert( myArr.length ) // 2 in all except IE

    方法借用

    如果你仅仅想用Array的几个方法的功能,那么是不必要继承Array的你可以borrow a method,然后apply it without inhereting, 比如

    var join = Array.prototype.join  或者更短一点 var join = [].join

    随后使用一个非标准的this 来调用它:

    var obj = {
      0: 'first',
      1: 'second',
      length: 2
    }
    
    alert( [].join.call(obj, ', ') ) // first, second

    上例中, Array.prototype.join在ES-5标准中描述,他并不检查对象type。它做的事情是一个对所有的属性来做joining loop。Array的方法常常用来被借用以便操作array-like对象。

    更改原生prototypes“修改”已经存在的原生方法

    很多时候,你希望通过修改原生的方法,获得你希望得到的特性,比如我们希望extend Array对象的join()方法,以便在将控制返回给原生join()方法之前增加一些log信息。

    Array.prototype.join = (function(_super) {
    
        // return our new `join()` function
        return function() {
            console.log("Hey, you called join!");
            return _super.apply(this, arguments);
        };         // Pass control back to the original join()
                   // by using .apply on `_super`
    
    })(Array.prototype.join);
    //                            
    // Pass the original function into our
    // immediately invoked function as `_super`
    // which remains available to our new
    // function thanks to JavaScript closures.

     借用原生代码简化重构自用的函数

    有时候,我们会嫌直接使用原生函数方法太过啰嗦冗长,一个有效的办法是借鸡生蛋,重新包装。比如我们常常需要就正则表达式来对一个字符串做test,可行的办法就是借用Function.bind返回一个有待运行的函数,后面直接简单调用:

    var testForA = RegExp.prototype.test.bind(/[aA]/);
    var testForDigit = RegExp.prototype.test.bind(/[0-9]/);
      // function () { [native code] }
    
      testForA('hello');
      // false
    
      testForA('hella');
      // true
    
      ['hello', 'hella', 'holla'].filter(testForA);
      // ['hella', 'holla']
    
      testForDigit('this has no digits');  //false
    testForDigit('this has 123 digits');  //true

    http://prototypejs.org/

    http://sugarjs.com/native

  • 相关阅读:
    什么是JAVA内容仓库(Java Content Repository)(3)
    Documentum 中 Type定义与存储
    洛谷 P1421 小玉买文具
    校内 第一届ACM校赛——热身赛
    洛谷 P1307 数字反转
    洛谷P1579 哥德巴赫猜想(升级版)
    51单片机 第三节 独立按键
    51单片机 第四节 数码管
    校内 第一届ACM校赛——正赛
    51单片机 第二节 点亮LED
  • 原文地址:https://www.cnblogs.com/kidsitcn/p/5141806.html
Copyright © 2011-2022 走看看