js原型及原型链

js对象与其它面向对象编程语言的差异

--  JAVA与C++等语言中的对象,是先构造一个类抽象事物,再通过类实例化一个个对象。但javascript中,中不区分类和实例的概念,而是通过原型(prototype)来实现面向对象的封装,继承和多态,从而实现面向对象编程。

什么是面向对象编程?

  • 将现实世界中各种复杂的关系,抽象为一个个对象,由对象的之间的分工和合作,完成对真实世界的模拟
  • 具有灵活性,代码可重用性,模块性等特点,容易维护和开发,适合多人合作的大型软件项目

什么是js原型?

  • 在JavaScript中,原型也是一个对象,原型所有的属性和方法都会被构造函数的实例继承
  • JavaScript的对象中都包含了一个__proto__内部属性,这个属性指向的就是该对象的原型

什么是构造函数?

  • 专门用来生成对象的函数,内部使用了this对象,对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上
  • 它描述了一些对象的基本结构,是一个模板 ,使生成的对象有相同的结构
  • 以区别普通函数,构造函数名字首字母大写
  • 构造函数中,若return的是原始数据,new命令会忽略该语句返回this对象;若return的是一个和this无关的新对象,new返回的会是该新对象。

什么是原型链?

--  构造函数有prototype属性,会指向一个原型对象,这样通过构造函数生成的实例就会继承来自原型对象的属性和方法,而原型对象又是它所属构造函数形成的实例,会继承来自它的原型对象的属性和方法,这样层层继承,直到原型顶部为null为止,这样原型层层连接起来,就构成了原型链。 如下图所示

prototype和__proto__区别?

  • 所有的对象,都有__proto__属性,这个属性指向该对象的原型
  • 所有函数对象,除了__proto__属性之外,还有prototype属性,该属性用来设置实例对象的__proto__属性(函数对象作为构造函数创建实例时,函数的prototype属性会赋值给实例属性__proto__指向原型) (构造函数.prototype === 实例对象.__proto__)
  • 所有原型对象都有"constructor"属性,该属性指向  创建了基于该原型的实例对象的  构造函数

什么是封装

--就是将属性和方法封装成一个对象,隐藏属性和方法的实现细节,仅对外公开接口。

js如何封装

简单的封装--原始模式生成实例对象

缺点:

  • 生成实例代码重复过多
  • 实例与原型之间关联不大

eg:

<script>
   // 创建一个cat原型对象
   // 把两个属性封装在一个对象里面
    var Cat = {    
      name: ‘‘,
      color: ‘‘    
    };
   // 生成两个实例对象
   var cat1 = {};       // 创建一个空对象   
   cat1.name = "大毛";  // 按照原型对象的属性赋值
   cat1.color = "黄色";    
   var cat2 = {};      
   cat2.name = "二毛";      
   cat2.color = "黑色";
</script>

原始模式--用函数生成实例对象

优点:

  • 改进了原始模式的一些代码重复的问题

缺点:

  • 实例与原型之间关联仍不大

eg:

<script>
   function Cat(name, color) {    
     return {      
       name: name,
      color: color     
     };     
   }
   var cat1 = Cat("大毛","黄色"); //调用函数生成实例对象
   var cat2 = Cat("二毛","黑色");
</script>

构造函数模式

优点:

  • 解决了原始模式代码重复的问题
  • 生成的实例对象与原型有着紧密的联系

缺点:

  • 共有不变的属性和方法,在每个实例中都会再次生成,会多占用一些内存。

    <script>
        function Cat(name, color) {    
          this.name = name;    
          this.color = color;  
        }
        var cat1 = new Cat("大毛", "黄色");    
        var cat2 = new Cat("二毛", "黑色");    
        alert(cat1.name); // 大毛    
        alert(cat1.color); // 黄色
        alert(cat1.constructor === Cat); //true
        alert(cat2.constructor === Cat); //true
        alert(cat1.hasOwnProperty("constructor")); //false;
        alert(cat1.__proto__.hasOwnProperty("constructor"));
        //true //constructor属性在原型中指向构造函数
    </script>

Prototype模式

优点:

  • 解决了原始模式代码重复的问题
  • 生成的实例对象与原型有着紧密的联系
  • 不变的属性和方法,在原型中被共享,减少了内存的占用,提高了运行效率

将不变的属性和方法,直接定义在prototype对象上

<script>
    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(); // 吃鱼
    alert(Cat.prototype.isPrototypeOf(cat1)); //true
    alert(Cat.prototype.isPrototypeOf(cat2)); //true
    alert(cat1.hasOwnProperty("name")); // true
    alert(cat1.hasOwnProperty("type")); // false
    alert("name" in cat1); // true
    alert("type" in cat1); // true
</script>

继承:子类可以使用父类的所有功能,并且对这些功能进行扩展。继承的过程,就是从一般到特殊的过程。

使用构造函数继承:

一  构造函数绑定

--用call或apply方法,将父对象的构造函数绑定在子对象,实现对父对象属性的继承

<script>
    function Animal() {    
      this.type = "动物";  
    }

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

    function Cat(name, color) {      
      Animal.apply(this);    
      this.name = name;      
      this.color = color;     
    }     
    var cat1 = new Cat("大毛", "黄色");   
    alert(cat1.type); // 动物
    alert(cat1.hasOwnProperty(‘type‘)); //true type已是实例中自身的属性
</script>

二、 prototype模式

<script>
    function Animal() {    
      this.type = "动物";  
    }

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

    Cat.prototype = new Animal();
    // 替换prototype对象(原先值被删除,被赋予一个新值)
    // 此时Cat.prototype.constructor也变为Animal
    Cat.prototype.constructor = Cat;
    // 将constructor属性指回原来的构造函数
    // 防止继承链的紊乱  
    var cat1 = new Cat("大毛", "黄色");   
    alert(cat1.type); // 动物
    alert(cat1.hasOwnProperty(‘type‘));
    //false  //type继承Animal一个实例属性,
    //Animal的一个实例为cat1原型
    alert(Animal.isPrototypeOf(cat1));        //false
    alert(Cat.prototype.isPrototypeOf(cat1)); //true
</script>

三  直接继承prototype

优点:效率比较高(不用执行和建立Animal的实例了),节省内存

缺点:Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。

<script>
    function Animal() {}
    Animal.prototype.type = "动物";
    function Cat(name, color) {       
      this.name = name;       
      this.color = color;     
    }
    Cat.prototype = Animal.prototype;
    //不用new Animal();
    Cat.prototype.constructor = Cat;
    //隐士改变Animal的构造函数变Cat
    var cat1 = new Cat("大毛", "黄色");   
    alert(cat1.type); // 动物
    alert(cat1.hasOwnProperty(‘type‘)); //false
    alert(Animal.prototype.isPrototypeOf(cat1)); //true
    alert(Animal.prototype.constructor); // Cat(){}
    //确认Animal的构造函数变为Cat
</script>

四、 利用空对象作为中介

<script>
    function Animal() {}
    Animal.prototype.type = "动物";

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

    function extend(Child, Parent) {   // YUI库如何实现继承的方法
      var F = function () {};  // 创建一个空函数对象  
      F.prototype = Parent.prototype;   
      Child.prototype = new F();    
      Child.prototype.constructor = Child;     
      Child.uber = Parent.prototype;
      // 备用属性指向父对象的prototype属性
      // 实现继承的完备性     
    }

    extend(Cat, Animal);  
    var cat1 = new Cat("大毛", "黄色");   
    alert(cat1.type);     // 动物
    alert(cat1.hasOwnProperty(‘type‘));  // false
    alert(Animal.prototype.isPrototypeOf(cat1)); // true
    alert(Animal.prototype.constructor); // Animal(){}
</script>

五、 拷贝继承

--  把父对象的所有属性和方法,拷贝进子对象,去实现继承

<script>
    function Animal() {}
    Animal.prototype.type = "动物";

    function Cat(name, color) {       
      this.name = name;       
      this.color = color;     
    }   //父对象的prototype属性一一拷贝给子对象的prototype属性中
    function extend2(Child, Parent) {    
      var p = Parent.prototype;    
      var c = Child.prototype;         
      for (var i in p) {              
        c[i] = p[i];             
      }         
      c.uber = p;        
    }
    extend2(Cat, Animal);     
    var cat1 = new Cat("大毛", "黄色");      
    alert(cat1.type); // 动物
    alert(cat1.hasOwnProperty(‘type‘)); //false
    alert(Animal.prototype.isPrototypeOf(cat1)); //false
    alert(Cat.prototype.isPrototypeOf(cat1)); //true
    alert(Animal.prototype.constructor); // Animal(){} 

</script>

非构造函数的继承

将两个非构造函数的普通对象形成继承关

object()方法:

<script>
    //将子对象的prototype属性,指向父对象
    //让子对象与父对象连在一起。
    function object(o) {
      function F() {}
      F.prototype = o;
      return new F();
    }
    var Chinese = {
      nation: ‘中国‘
    };

    var Doctor = object(Chinese);
    Doctor.career = ‘医生‘;
    alert(Doctor.nation); //中国
</script>
<script>
    function SuperType() {
      this.colors = ["red", "blue", "green"];
    }
    function SubType() {
      SuperType.call(this);   //继承了SuperType
    }
    var instance1 = new SubType();
    instance1.colors.push("black");   //实例1中,属性color加一属性值black
    alert(instance1.colors);          //"red,blue,green,black"
    var instance2 = new SubType();
    alert(instance2.colors);          //"red,blue,green"
</script>

构造函数模式的一些问题:

  • 共有不变的属性和方法,在每个实例中都会再次生成,会多占用一些内存
  • 可以将共有的属性和方法,在内存中只生成一次,然后所有实例都指向那个内存地址

浅拷贝:只拷贝对象中基本类型的数据 

<script>
    function extendCopy(p) {    
      var c = {};    
      for (var i in p) {      
        c[i] = p[i]; 
        //复制成另一对象的一副本,且对象中的属
        //性若是地址也会相同的复制过来,造成一些属性对象的享        //可能会影响到父对象属性, 
      }    
      c.uber = p;    
      return c;  
    }

    var Chinese = {
      nation: ‘中国‘
    };

    var Doctor = extendCopy(Chinese);    
    Doctor.career = ‘医生‘;  
    alert(Doctor.nation); // 中国
</script>

深拷贝:

<script>
    function deepCopy(c, p) {   
      var c = c || {};  
      for (var i in p) {     
        if (typeof p[i] === ‘object‘) {        
          c[i] = (p[i].constructor === Array) ? [] : {};   
          deepCopy(p[i], c[i]);  //递归调用"浅拷贝",确定对象最终的基本属性  
        } else {      
          c[i] = p[i];    
        }    
      }
      return c;
    }

    var Chinese = {
      nation: ‘中国‘
    };

    var Doctor = deepCopy(Chinese)
</script>

原型链继承的一些问题:

  • 当原型变成另一个类型的实例时,该原型会拥有上一类型的属性,会共享下去
  • 创建子类型实例时,没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。

eg:

<script>
    function SuperType() {
      this.colors = ["red", "blue", "green"];
    }
    function SubType() {}
    //继承了SuperType
    SubType.prototype = new SuperType();
    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors); //"red,blue,green,black"
    var instance2 = new SubType();
    alert(instance2.colors); //"red,blue,green,black"
    //这样SubType.prototype会有一个colors属性,这样所有的SubType会共享这一实例,造成一些错误,你不知道还好,一直到
</script>  

六、 Prototype模式的验证方法

  • isPrototypeOf() 判断某个proptotype对象和某个实例之间的关系。  //SubType.prototype.isPrototypeOf(incetence1); //true
  • hasOwnProperty()   判断某一个属性到底是本地属性,还是继承自prototype对象的属性。// incetence1.hasOwnProperty("color"); // true
  • in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。"color" in inctence1  //true

原文地址:https://www.cnblogs.com/fermin/p/9103047.html

时间: 2024-08-03 07:29:22

js原型及原型链的相关文章

JS原型与原型链终极讲解

function Person () { this.name = 'John'; } var person = new Person(); Person.prototype.say = function() { console.log('Hello,' + this.name); }; person.say();//Hello,John 上述代码非常简单,Person原型对象定义了公共的say方法,虽然此举在构造实例之后出现,但因为原型方法在调用之前已经声明,因此之后的每个实例将都拥有该方法.从

JS原型与原型链终极详解 (转载)

这篇文章需要认认真真仔仔细细的看才能看懂 一. 普通对象与函数对象  JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object ,Function 是JS自带的函数对象.下面举例说明 function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)'); var o3 = new f1(); var o1 = {}; var o2 =new Object

JS原型与原型链终极详解

一. 普通对象与函数对象  JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object ,Function 是JS自带的函数对象.下面举例说明 function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)'); var o3 = new f1(); var o1 = {}; var o2 =new Object(); console.log(typeo

js中原型和原型链理解

js中属性的继承以及查找都会用到原型链的知识,对于深入学习js的人来说是一个难点也是一个重点,下面梳理下对于原型以及原型链的理解. 首先,我们要知道什么是原型以及原型链?他们有什么样的作用? 可以理解为JS对象在创建时都会与之关联另一个对象,这就是我们所说的原型,每一个对象都会从原型"继承"属性.下图表示了构造函数与实例原型的关系,其中Object.prototype表示实例原型. 那么实例与实例原型又是怎么联系的呢?接下来又要说到另一个属性__proto__,每一个JS对象都有一个属

深入探究js的原型与原型链

最近在看<javascript高级程序设计>,看完之后,觉得感触,然后我今天又看到了一篇文章,说的很搞笑.就想整理下自己所学的. 首先,如果我们把ECMAScript的对象想象为散列表,即一组名值对,其中值可以是数据或函数. 那究竟对象.原型对象.构造函数.继承.原型链. 原型属性的共享.原型的动态性.原型的整体重写呢,来一组简单粗暴的描述哈,还是挺搞笑的. 1)人是人他妈生的,妖是妖他妈生的.人和妖都是对象实例,而人他妈和妖他妈就是原型.原型也是对象,叫原型对象. 2)人他妈和人他爸啪啪啪能

总结一下js的原型和原型链

最近学习了js的面向对象编程,原型和原型链这块是个难点,理解的不是很透彻,这里搜集了一些这方面的资料,以备复习所用 一. 原型与构造函数 Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型.这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型.譬如普通函数: function F(){ ; } alert(F.prototype instanceof Object) //true 构造函数,也即构造对象.首先了解下

JS原型与原型链(好文看三遍)

一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object ,Function 是JS自带的函数对象. 下面举例说明: function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)'); var o3 = new f1(); var o1 = {}; var o2 =new Object(); console.log(type

深入理解JS继承和原型链

对于那些熟悉基于类的面向对象语言(Java 或者 C++)的开发者来说,JavaScript 的语法是比较怪异的,这是由于 JavaScript 是一门动态语言,而且它没有类的概念( ES6 新增了class 关键字,但只是语法糖,JavaScript 仍旧是基于原型). 涉及到继承这一块,Javascript 只有一种结构,那就是:对象.在 javaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接.这个原型对象又有自己的原型,直到某个对象的原型为null 为止

JS原型与原型链终极详解(转)

一. 普通对象与函数对象   JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object ,Function 是JS自带的函数对象.下面举例说明 function f1(){};  var f2 = function(){};  var f3 = new Function('str','console.log(str)'); var o3 = new f1();  var o1 = {};  var o2 =new Object(); console.log(

JS原型和原型链

1 var decimalDigits = 2, 2 tax = 5; 3 4 function add(x, y) { 5 return x + y; 6 } 7 8 function subtract(x, y) { 9 return x - y; 10 } 11 12 //alert(add(1, 3)); 但是,这个并不能体现OOP思想,看了原型与原型链之后觉得OOP一目了然: 1 var Calculator = function (decimalDigits, tax) { 2 th