• 面向对象编程


          //自己手写案例
          function
    Fruits(){ // 构造函数 } Fruits.prototype={ // 原型函数 name:"Joe", age:16, init:function(){ console.log('hello world') } } Apple=new Fruits(); //实例对象 Apple.init();//hello world Apple.init=function(){ console.log('我是实例方法'); } Apple.init();//我是实例方法 // console.log(Apple.__proto__) //实例可以通过原型链找到原型 console.log(Apple) console.log(Fruits.prototype) // console.log(Apple.constructor == Fruits) //false(如果没有原型对象,此处会为true,应该每一个实例都有一个constructor属性,默认调用prototype对象的constructor属性
    //          console.log(Apple instanceof Fruits) //true  Apple是Fruits原型对象的实例对象

      以上案例,包括了构造函数,原型函数和实例对象,实例对象可以通过__proto__原型链找到原型,实例对象也可以自己定义函数,比如例子中的Apple.init,此时实例对象本身和原型对象上都有init帆方法,此时实例对象会优先调用本身的 方法,如果没有,再去prototype上找。

    理解原型对象

      无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。

           function New(){
                    
                }
                console.log(New.prototype)
           console.log(New.prototype.constructor==New) //true

    而通过这个构造函数,我们还可以为原型对象添加其他的属性和方法。

      创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性,至于其他方法,则都是从Object继承来的。当构造函数创建了一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。

    ECMA-262第5版中管这个指针交[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox,Safari和Chrome在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本则是完全不可见的。不过,要

    明确的真正重要的一点是,这个连接存在于实例和构造函数的原型对象之间,而不是存在于实例与构造函数之间。(换句话说,实例对象与构造函数没有直接的关系)

    方法:

    isPrototypeOf()

    如果[[Prototype]]指向调用isPrototypeOf()方法的对象(New.prototype),那这个方法就返回true

          function New(){
                    
                }
                New.prototype={
                    name:'jane',
                    getName:function(){
                        console.log(this.name);
                    }
                }
                var fn=new New();
                console.log(New.prototype.isPrototypeOf(fn));//true

    在ECMAScript5中新增了一个方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值,例如:

    Object.getPrototypeOf(fn)==New.prototype;//true
    Object.getPrototypeOf(fn).name;//jane

    Object.getPrototypeOf()返回的对象实际就是这个对象的原型。使用这个方法可以方便的取得一个对象的原型。

    每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先是从对戏那个实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中

    查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。

    首先js中对象(函数(function)也是对象),可以访问到原型(prototype),
    然后你通过对象得到的原型(prototype)中也会有一个给对象的指针(即constructor)
    例如 :
    function Person(){};
    var pro = Person.prototype;//得到对象的原型 
    var obj = pro.constructor ; // 这样可以得到Person对象。
    对象创建对象的实例就不多说了 
    var p = new Person(); // 创建一个对象实例 。
    对象实例中也会隐含的包含一个指向prototype的指针(在safrai ,firefox,chrome,opera等浏览器中可以使用 '__proto__'来访问)
    例如:
    var p2 = new Person();
    var proro = p2.__proto__ ;// 可以得到对象原型(prototype)的引用 ,
    var bool = p2.__proto__ == Person.prototype;
    alert(bool); // true 对象的原型和实例获取的原型是同一个对象,
    总结: 
    对象和实例都可以访问到原型 
    对象:Person.prototype;
    实例:p.__proto__ ;
    原型可以访问到对象
    Person.prototype.constructor;
    则实例也可以访问到对象
    p.__proto__.constructor ;

    读阮一峰文章总结

    一 生成对象的原始模式

     假设把一个猫看成一个对象,它有‘颜色’和‘名字’两个属性,

    var Cat={
      name:''.
      color:''    
    }

    现在我们需要根据原型对象的规格,生成两个实例

    var cat1 = {}; // 创建一个空对象
        cat1.name = "大毛"; // 按照原型对象的属性赋值
        cat1.color = "黄色";
    var cat2 = {};
        cat2.name = "二毛";
        cat2.color = "黑色";

    好了,这就是最简单的封装了,把两个属性封装在一个对象里面。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;而是实例与原型之间,没有任何的办法,可以看出有什么联系。

    二  原始模式的改进

    我们可以写一个函数,解决代码重复的问题

           function Cat(name,color){
                    return{
                        name:name,
                        color:color
                    }
                }            

    然后生成实例对象,就等于是在调用函数

    var cat1=new Cat('大黄','黄色');
    console.log(cat1)//{name: "大黄", color: "黄色"}
    var cat2=new Cat('二黄','白色');
    console.log(cat1)//{name: "二黄", color: "白色"}

    这种方法的问题依然是,cat1和cat2之间没有内在的联系,不能反映出它们是同一原型对象的实例。

    三 构造函数模式

    为了解决从原型对象生成实例的问题,javascript提供了一个构造函数(Constructor)模式

    所谓构造函数,其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new 构造符,就能生成实例,并且this变量会绑定在实例对象上。

    比如,猫的原型对象 现在可以这样写,

     function Cat(name,color){
        this.name=name;
        this.color=color;
      }

    现在就可以生成实例对象了。

      var cat1 = new Cat("大毛","黄色");
      var cat2 = new Cat("二毛","黑色");
      alert(cat1.name); // 大毛
      alert(cat1.color); // 黄色

    这时cat1cat2会自动含有一个constructor属性,指向它们的构造函数。

    alert(cat1.constructor == Cat); //true
    alert(cat2.constructor == Cat); //true

    Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。

    alert(cat1 instanceof Cat); //true
    alert(cat2 instanceof Cat); //true

    四 构造函数的问题

    构造函数方法很好用,但是存在一个浪费内存的问题。

    请看,我们现在为Cat对象添加一个不变的属性type(种类),再添加一个方法eat(吃)。那么,原型对象Cat就变成了下面这样:

    function Cat(name,color){
        this.name = name;
        this.color = color;
        this.type = "猫科动物";
        this.eat = function(){alert("吃老鼠");};
      }
     //还是采用同样的方法生成实例
    var cat1 = new Cat("大毛","黄色");   var cat2 = new Cat ("二毛","黑色");   alert(cat1.type); // 猫科动物   cat1.eat(); // 吃老鼠

    表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。

      alert(cat1.eat == cat2.eat); //false

    能不能让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。

    五、 Prototype模式

    Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

    这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。

      function Cat(name,color){

        this.name = name;

        this.color = color;

      }

      Cat.prototype.type = "猫科动物";

      Cat.prototype.eat = function(){alert("吃老鼠")};

    然后,生成实例。

      var cat1 = new Cat("大毛","黄色");

      var cat2 = new Cat("二毛","黑色");

      alert(cat1.type); // 猫科动物

      cat1.eat(); // 吃老鼠

    这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。

      alert(cat1.eat == cat2.eat); //true

    六、 Prototype模式的验证方法

    为了配合prototype属性,Javascript定义了一些辅助方法,帮助我们使用它。,

    6.1 isPrototypeOf()

    这个方法用来判断,某个proptotype对象和某个实例之间的关系。

      alert(Cat.prototype.isPrototypeOf(cat1)); //true

      alert(Cat.prototype.isPrototypeOf(cat2)); //true

    6.2 hasOwnProperty()

    每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

      alert(cat1.hasOwnProperty("name")); // true

      alert(cat1.hasOwnProperty("type")); // false

    6.3 in运算符

    in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。

      alert("name" in cat1); // true

      alert("type" in cat1); // true

    in运算符还可以用来遍历某个对象的所有属性。

      for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }

     七  原型链

    我们知道所有的函数都有一个叫做toString的方法。那么这个方法到底是在哪里的呢?

    先随意声明一个函数:

    function add() {}
    

    那么我们可以用如下的图来表示这个函数的原型链。

    原型链

    其中add是Function对象的实例。而Function的原型对象同时又是Object原型的实例。这样就构成了一条原型链。原型链的访问,其实跟作用域链有很大的相似之处,

    他们都是一次单向的查找过程。因此实例对象能够通过原型链,访问到处于原型链上对象的所有属性与方法。这也是foo最终能够访问到处于Object原型对象上的toString方法的原因。

    基于原型链的特性,我们可以很轻松的实现继承。

    参考:

    http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html

    http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html

    http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.html

  • 相关阅读:
    js对象数组(JSON) 根据某个共同字段 分组
    ajax如何处理服务器返回的三种数据类型
    JavaScript中常见排序算法详解
    《JavaScript语言精粹》读书笔记——给类型增加方法一节的疑问
    前端常见跨域解决方案(全)
    ACE模板的布局简介
    ES6学习笔记---对象的扩展
    偏振
    欧拉赞词
    狄拉克δ函数的导数
  • 原文地址:https://www.cnblogs.com/xumqfaith/p/7999834.html
Copyright © 2020-2023  润新知