• js中的原型


    一:原型属性

    函数本身也是一个包含了方法和属性的对象。

     定义一个函数foo(),访问其他对象一样访问该函数的属性:

    function foo(a, b) {
        return a * b;
    }
    foo.length
    --2
    
    foo.constructor
    ƒ Function() { [native code] }
    

     函数定义时被创建的属性就包括有prototype属性,他的初始值是一个“空”对象

    typeof foo.prototype
    "object"
    

     可以自己添加该属性

    foo.prototype={};
    

     1.利用原型添加方法和属性

    构建一个具体的构造函数Gadget(),在新建时为其添加属性和方法。

    function Gadget(name, color) {
        this.name = name;
        this.color = color;
        this.whatAreYou = function () {
            return 'I am a ' + this.color + '' + this.name;
        };
    }
    

     通过构造器函数的prototype属性来增加该构造器所能提供的功能

    Gadget.prototype.price = 100;
    Gadget.prototype.rating = 3;
    Gadget.prototype.getInfo = function () {
        return 'Rating:' + this.rating + ',price: ' + this.price;
    }
    

     也可以另外定义一个对象,将其覆盖到之前的原型上:

    Gadget.prototype = {
        price: 100,
        rating:
        }
    

     2.使用原型的方法和属性:

    直接用该构造器来新建对象,就可以访问之前定义的那些属性和方法。

    var newtoy=new Gadget('webcam','black');
    
    newtoy.name;
    "webcam"
    
    newtoy.color;
    "black"
    
    newtoy.whatAreYou();
    "I am a blackwebcam"
    
    newtoy.price;
    100
    
    newtoy.rating;
    3
    
    newtoy.getInfo();
    "Rating:3,price: 100"
    

     可以随时修改prototype属性,并且由同一构造器创建的所有对象的prototype属性也同时改变。

    向原型中添加一个新方法:

    Gadget.prototype.get = function (what) {
        return this[what];
    };
    

     newtoy对象在get()方法定义之前就已经被创建,我们依然可以在该对象中访问新增的方法:

    newtoy.get('color');
    "black"
    newtoy.get('price');
    100
    

     3.自身属性与原型属性

    在getInfo()的示例中,我们使用this指针来完成对象的访问,但其实直接引用Gadget.prototype也可以完成同样的操作:

    Gadget.prototype.getInfo = function () {
        return 'Rating: ' + Gadget.prototype.rating +
            ',price: ' + Gadget.prototype.price;
    };
    

     回到之前的那个newtoy对象上:

    var newtoy = new Gadget('webcam', 'black');
    
     newtoy.name
    "webcam"
    
    newtoy.rating
    3
    

     每个对象都有属于自己的构造器属性,所引用的就是用于创建该对象的那个函数

    newtoy.constructor===Gadget
    true
    
    newtoy.constructor.prototype.rating;
    3
    

     每个对象都会有一个构造器,而原型本身也是一个对象,这意味着它也必然有一个构造器,而这个构造器又会有自己的原型,这种结构可能会一直不断的持续下去,并最终取决于原型链的长度,但最后一环肯定是Object内建对象,它是最高级的父级对象。

    4.利用自身属性重写原型属性

    如果一个对象自身属性中没有找到指定的属性,就会使用原型链中查找到的相关属性,如果,对象的自身属性与原型属性同名,自身属性的优先级高于原型属性。

    示例:同一个属性名同时出现在对象的自身属性和原型属性中:

    function Gadget(name) {
        this.name = name;
    }
    Gadget.prototype.name = 'sunyajing';
    

     新建一个对象,并访问该对象自身的name属性

        var toy = new Gadget('camera');
            toy.name;
    "camera"

     通过hasOwnProperty()判断一个属性是自身属性还是原型属性

    toy.hasOwnProperty('name');
    true
    

     删除这个属性,同名的原型属性就会“浮出水面”

        delete toy.name;
        true
    
    toy.name;
    "sunyajing"
    
    toy.hasOwnProperty('name');
    false
    

     我们随时可以重建这个对象的自身属性:

       toy.name = 'camera';
        toy.name;
    "camera"
    

     判读一个对象的某个原型属性到底是原型链中哪个原型的属性呢?可以用hasOwnProperty()属性:

     toy.toString();
    "[object Object]"
    
     toy.hasOwnProperty('toString');
    false
    
    toy.constructor.hasOwnProperty('toString');
    false
    
     toy.constructor.prototype.hasOwnProperty('toString');
    false
    
    Object.hasOwnProperty('toString');
    false
    
        Object.prototype.hasOwnProperty('toString');
    true
    

     for 更适合数组,for-in更适合对象,构造URL字符串为例:

     var params = {
            productid: 666,
            section: 'products'
        };
        var url = 'http://example.org/page.php',
            i,
            query = [];
        for (i in params) {
            query.push(i + '=' + params[i]);
        }
        url += query.join('&');
    "http://example.org/page.phpproductid=666&section=products"
    

     并不是所有的属性都会在for-in循环中显示,例如(数组的)length属性和constructor属性就不会被显示

    1.会显示的属性被称为可枚举的,可以通过propertyIsEnumerable()方法来判断对象的某个属性是否可枚举。

    2.原型链中的各个原型属性也会被显示出来,当然前提是可枚举的,可以通过对象的hasOwnProperty()方法来判断一个属性是对象自身属性还是原型属性。

    3.对于所有的原型属性propertyIsEnumerable()都会返回false,包括那些在for-in循环中可枚举的属性

    通过以下代码看一下具体的实现方法:

      function Gadget(name, color) {
            this.name = name;
            this.color = color;
            this.getName = function () {
                return this.name;
            };
        }
        Gadget.prototype.price = 100;
        Gadget.prototype.rathing = 3;
    

     对他执行for-in循环,就会列出该对象中的所有属性,包括原型中的属性:

     for (var prop in newtoy) {
            console.log(prop + ' = ' + newtoy[prop]);
        }
    

     结果:

    name = webcam
    color = black
    getName = function () {
                return this.name;
            }
    price = 100
    rathing = 3

     如果要对对象属性和原型属性做一个区分,就要调用hasOwnProperty()方法:

    newtoy.hasOwnProperty('name');
    true
    newtoy.hasOwnProperty('price');
    false
    

     显示对象的自身属性:

      for (var prop in newtoy) {
            if (newtoy.hasOwnProperty(prop)) {
                console.log(prop + ' = ' + newtoy[prop]);
            }
        }
    

     结果:

    name = webcam
     color = black
     getName = function () {
                return this.name;
            }
    

     propertyIsEnumerable()会对所有的非内建对象属性返回true

    newtoy.propertyIsEnumerable('name');
    true
    

     对于内建属性和方法大部分是不可枚举的

    newtoy.propertyIsEnumerable('constructor');
    false
    

     如果propertyIsEnumerable()的调用是来自原型链上的某个对象,那么该对象中的属性是可枚举的

     newtoy.constructor.prototype.propertyIsEnumerable('price');
    true
    

     5.isPrototypeOf()方法

    每个对象中都会有一个isPrototypeOf()方法,这个方法告诉我们当前对象是否是另一个对象的原型

     var monkey = {
            hair: true,
            feeds: 'bananas',
            breathes: 'air'
        };
    

     创建一个Human的构造器函数,并设置原型属性指向monkey:

        Human.prototype = monkey;
    
    Human.prototype = monkey;
        var george = new Human('George');
        monkey.isPrototypeOf(george);
    

     不知道某个原型的情况下,获得对象的原型。

        Object.getPrototypeOf(george).feeds;
        Object.getPrototypeOf(george) === monkey;
    

     6.神秘的_proto_链接

    改写用monkey对象做原型的Human()对象构造器。

      var monkey = {
                feeds: 'bananas',
                breathes: 'air'
            };
            function Human() { }
            Human.prototype = monkey;
    

     创建一个developer对象,并赋予它一些属性:

            var developer = new Human();
            developer.feeds = 'pizza';
            developer.hacks = 'JavaScript';
    

     访问:

           developer.hacks;
          -- "JavaScript"
            developer.feeds;
          -- "pizza"
    

     但是breathes在developer对象自身的属性中是不存在的,所以就得去原型中查找

    developer.breathes;
    "air"
    

     这个神秘的链接叫做__proto__属性

    developer.__proto__ === monkey;
    true
    

     __proto__是某个实例对象的属性,而prototype则是属于构造器函数的属性。

    typeof developer.__proto__
    "object"
    
    typeof developer.prototype;
    "undefined"
    
     typeof developer.constructor.prototype;
    "object"
    

     二:扩展内建对象

    内建对象的构造器函数(Array、String、Object、Function)都是可以通过原型来扩展的

      Array.prototype.inArray = function (needle) {
                for (var i = 0, len = this.length; i < len; i++) {
                    if (this[i] === needle) {
                        return true;
                    }
                }
                return false;
            };
    
            var colors = ['red', 'green', 'blue'];
            colors.inArray('red');
    true
    
    colors.inArray('yellow');
    false
    

    反转字符串的功能:

      String.prototype.reverse = function () {
                return Array.prototype.reverse.apply(this.split('')).join('');
            }
    
            "sunyujing".reverse();
    "gnijuynus"
    

     为String对象添加trim()方法

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

     如果想要通过原型为某个对象添加一个新属性,务必检查一下该属性是否已经存在

    三:原型陷阱

    处理原型时,特别注意以下两种行为:

    1.我们对原型对象执行完全替换时,可能会触发原型链中某种异常(exception)

    2.prototype.constructor属性不可靠

    新建一个简答的构造器函数,并创建两个对象

     function Dog() {
                this.tail = true;
            }
            var benji = new Dog();
            var rusty = new Dog();
    

     添加一个Say()方法:

      Dog.prototype.say = function () {
                return 'Woof';
            };
    

     上面的两个对象都可以访问该新方法。

    benji.say();
    "Woof"
    
    rusty.say();
    "Woof"
    

     检查一下这些对象构造器函数,会发现一切正常。

    benji.constructor === Dog;
    --true
    rusty.constructor === Dog;
    --true
    

     用一个自定义的新对象完全覆盖掉原有的原型对象:

     Dog.prototype = {
                paws: 4,
                hair: true
            };
    

     这会使原有对象不能访问原型的新增属性,依然通过那个神秘的链接与原有的原型对象保持联系。

     typeof benji.paws;
    --"undefined"
    
    benji.say();
    --"Woof"
    
     typeof benji.__proto__.say;    
    --"function"
    
       typeof benji.__proto__paws;
    --"undefined"
    

     之后创建的所有对象使用的都是被更新后的prototype对象

     var lucy = new Dog();
            lucy.say();
    Uncaught TypeError: lucy.say is not a function
    
     lucy.paws;
    --4
    

     其神秘的链接__proto__也指向了新的prototype对象

           typeof lucy.__proto__.paws;
           ---"number"
           typeof lucy.__proto__.say;
           ---"undefined"
    

     新对象的constructor属性就不能再保持正确了,原本是Dog()的引用却指向了Object()

    lucy.constructor;
    --ƒ Object() { [native code] }
    
    benji.constructor;
    --ƒ Dog() {
                this.tail = true;
            }
    

     也可以通过重新设置constructor属性来解决上述所有的异常行为:

    function Dog() { }
            Dog.prototype = {};
            new dog().constructor === Dog;
    --false
    
      Dog.prototype.constructor = Dog;
            new Dog().constructor === Dog;
    true
    

     当我们重写某个对象的prototype时,需要重新设置相应的constructor属性。

  • 相关阅读:
    counter的使用
    Keras保存模型
    pytorch中F.avg_pool1d()和F.avg_pool2d()
    为什么要进行batchNormalization?
    利用Ajax完成前后端数据传输有关知识(day67)
    ORM中choice参数的设计、Ajax介绍(day66)
    聚合分组查询、F与Q查询、常用字段知识(day65)
    ORM数据库查询操作(单表,多表)(day64)
    CBV源码剖析,模板语法(day63)
    小程序加入购物车抛物线效果
  • 原文地址:https://www.cnblogs.com/sunliyuan/p/8519474.html
Copyright © 2020-2023  润新知