Home > Web Front-end > JS Tutorial > JavaScript object-oriented and prototype_javascript skills

JavaScript object-oriented and prototype_javascript skills

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
Release: 2016-05-16 16:04:44
Original
1252 people have browsed it

ECMAScript has two development models: 1. Functional (procedural); 2. Object-oriented (OOP);

1 Create object
1. Ordinary creation of objects

// 创建一个对象,然后给这个对象新的属性和方法;
  var box = new Object();       // 创建一个Object对象;
  box.name = 'lee';          // 创建一个name属性并赋值;
  box.age = 100;
  box.run = function(){        // 创建一个run()方法并返回值;
    return this.name+this.age+'运行中...';
  } 
  console.log(box.run());       // 输入属性和方法的值;
// 缺点:想创建类似的对象,就会产生大量的代码;
Copy after login

2. Create objects in factory mode

// 这种方法就是为了解决实例化对象产生大量代码重复的问题;
  function createObject(name,age){    // 集中创建函数体;
    var obj = new Object;         // 函数体内创建Object;
    obj.name = name; 
    obj.age = age;
    obj.run = function(){
      return this.name+this.age+"运行中...";
    };
    return obj;
  }
  var box1 = createObject("lee",100);   // 实例化;调用函数并传参;
  var box2 = createObject("jack",200);  // 实例二;
  console.log(box1.run()+box2.run());   // 实例保持相对独立;
// 缺点:对象与实例的识别问题;无法搞清楚它们到底是那个对象的实例;
  console.log(typeof box1);        // Object;
Copy after login

3. Constructor creates object

// ECMAScript采用构造函数(构造方法)可用来创建特定的对象;
  function Box(name,age){          // 构造函数模式;
    this.name = name;           // this代表对象Box;
    this.age = age;
    this.run = function(){
      return this.name+this.age+"运行中...";
    };
  }
  var box1 = new Box("lee",100);     // 要创建对象的实例必须用new操作符;
  var box2 = new Box("jack",200);    // box1和box2都是Box对象的实例;
  console.log(box1 instanceof Box);   // true;很清晰的识别box1从属于Box;
// 使用构造函数,即解决了重复实例化的问题,有解决了对象识别的问题;
Copy after login

The difference between using constructor and factory pattern:
(1). The constructor method does not display the created object (new Object);
(2). Directly assign properties and methods to this object;
(3). No return statement;1 //Constructor specification:
(1). The function name (function Box) and the instantiation construct name (new Box) are the same and capitalized;
(2). To create an instance object through the constructor, you must use the new operator;

// 构造函数和普通函数的区别:
  var box = new Box('lee',100);        // 构造模式调用;
  Box('lee',200);               // 普通模式调用,无效;

  var o = new Object();
  Box.call(o,'jack',200);           // 对象冒充调用;
  // 将Box对象作用域扩充到对象o;Box()方法的运行环境已经变成了对象o里;
Copy after login

Problem with constructor:
When using the constructor to create each instance, the methods in the constructor must be recreated on each instance;
Because functions in ECMAScript are objects, every time a function is defined, an object is instantiated;
Creating functions in this way results in different scope chains and identifier resolution;

Two Prototypes
//Every function we create has a prototype attribute, which is an object;

// Purpose: Contains properties and methods that can be shared by all instances of a specific type;

// Understanding: prototype is the prototype object of the object created by calling the constructor;

// The advantage of using a prototype is that all object instances can share the properties and methods it contains;

// In other words, you don’t have to define object information (properties/methods) in the constructor, but you can directly add this information to the prototype;

1. Prototype mode (prototype adds attributes and methods)

1.原型模式
  function Box(){}                // 声明构造函数;
  Box.prototype.name = 'Lee';           // 在原型里添加属性和方法;
  Box.prototype.age = 100;
  Box.prototype.run = function() {
    return this.name+this.age+'运行中...';
  };
  var box1 = new Box();
  var box2 = new Box();
  console.log(box1.run==box2.run);        // =>true;方法引用的地址保持一致;
// 在原型中多了两个属性,这两个原型属性都是创建对象时自动生成的;
// 1.__proto__:构造函数指向原型对象的一个指针;它的作用:指向构造函数的原型的属性constructor;
 14// IE浏览器在脚本访问__proto__会不能识别; 15 
// 判断一个实例对象是否指向了该构造函数的原型对象,可以使用isPrototypeOf()方法来测试;
  console.log(Box.prototype.isPrototypeOf(box));  // =>true; 只要实例化对象,即都会指向;

// 原型模式的执行流程:
// 1.先查找构造函数对象的实例里的属性或方法,若有,立刻返回;
// 2.若构造函数对象的实例里没有,则去它的原型对象里找,若有,就返回;

// 虽然我们可以通过对象实例访问保存在原型中的值,但却不能访问通过对象实例重写原型中的值;
  var box1 = new Box();
  console.log(box1.name);              // Lee; 原型里的值;
  bo1.name = 'jack';
  console.log(box1.name);              // Jack;实例自己赋的值;
  var box2 = new Box();
  console.log(box2.name);              // Lee;原型里的值;没有被box1修改;
  // 如果想要box1继续访问原型里的值,可以把构造函数里的属性删除即可;
  delete box1.name;                 // 删除实例自己的属性;
  console.log(box1.name);              // Lee; 原型里原来的值;
Copy after login

2. Prototype and in operator

How to determine whether the property is in the instance of the constructor or in the prototype? You can use the hasOwnProperty() function to verify;
console.log(box.hasOwnProperty('name')); // If there is an instance, return true, otherwise return false;
The in operator will return true when the given property can be accessed through the object, whether the property exists in the instance or the prototype;
console.log('name' in box); // =>true, exists in the instance or prototype; 3. Simpler prototype syntax (prototype literal mode)

3. Simpler prototype syntax (prototype literal mode)

  function Box(){};
  Box.prototype = {                 // 以字面量形式创建包含属性和方法的新对象;
    name:'Lee',
    age:100,
    run:function(){
      return this.name+this.age+'运行中...';
    }
  };

// 使用构造函数创建原型对象和使用字面量创建原型对象在使用上基本相同;
// 但是,使用字面量创建的原型对象使用constructor属性不会指向实例,而是指向原型对象Object;构造函数的方式则相反;
  var box = new Box();
  console.log(box instanceof Box);
  console.log(box instanceof Object);  
  console.log(box.constructor == Box);      // 字面量方式,返回false;
  console.log(box.constructor == Object);     // 字面量方式,返回true;
  // 如果想让字面量方式的constructor指向实例对象:
  Box.prototype = {
    constructor:Box,              // 直接强制指向即可;
  }

  // PS:字面量方式为什么constructor会指向Object?
  // 因为Box.prototype={}这种字面量写法就是创建一个新对象;
  // 而每创建一个函数,就会同时创建它的prototype,这个对象也会自动获取constructor属性;
  // 所以,新对象的constructor重写了Box原来的constructor,因此指向了新对象,
  // 那个新对象没有指定构造函数,那么就默认为是Object;
Copy after login

4. Dynamic nature of prototype (rewriting will overwrite previous content)

// 原型的声明是有先后顺序的,所以,重写的原型会切断之前的原型;
  function Box(){};
  Box.prototype = {
    constructor:Box,
    name:'Lee',
    age:100,
    run:function(){
      return this.age+'运行中...';
    }
  };
  Box.prototype = {                // 原型重写了,覆盖了之前的原型;
    age:200,
    run:function(){
      return this.age+'运行中...';
    }
  }
  var box = new Box();
  console.log(box.run());              // =>200运行中...;
  // 重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;对象实例引用的仍然是最初的原型;
Copy after login

5. Prototype of native object

// Prototype objects can not only be used in custom objects, but also ECMAScript built-in reference types can be used in this way,
// And the built-in reference type itself also uses prototypes;
console.log(Array.prototype.sort); // =>function sort() { [native code] };
console.log(String.prototype.substring); // =>function substring() { [native code] };

6. Problems with prototype objects

// 原型模式创建对象缺点:省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的;
// 而原型最大的有点就是共享,属性共享;
// 但是,如果原型中的属性包含引用类型(对象),共享就会存在一定问题;
  function Box(){};
  Box.prototype = {
    constructor:Box,
    name:'Lee',
    age:100,
    family:['father','mother'],
    run:function(){
      return this.name+this.age+this.family;
    }
  };
  var box1 = new Box();
  box1.family.push('sister');           // 为box1的family属性添加了sister;而这个属性被共享到原型了;
  console.log(box1.run());            // =>Lee100father,mother,sister;
  var box2 = new Box();
  console.log(box2.run());            // =>Lee100father,mother,sister;
  // 数据共享导致实例化出的数据不能保存自己的特性;
Copy after login

7. Use a combination of constructor pattern (data not shared by objects) and prototype pattern (data shared by objects)

// 为了解决构造传参和共享问题,组合构造函数+原型模式:
  function Box(name,age){             // 不共享的使用构造函数;
    this.name = name;
    this.age = age;
    this.family = ['father','moter'];
  };
  Box.prototype = {                // 共享的使用原型模式;
    constructor:Box,
    run:function(){
      return this.name+this.age+this.family;
    }
  };
  // PS:这种混合模式很好的解决了传参和引用共享的大难题;是创建对象比较好的方法;
Copy after login

8. Dynamic prototype mode (encapsulate the prototype into the constructor)

// 原型模式,不管是否调用了原型中的共享方法,它都会初始化原型中的方法;
// 并且在声明一个对象时,构造函数+原型让人感觉怪异;最好把构造函数和原型封装到一起;
  function Box(name,age){              // 将所有信息封装到构造函数体内;
    this.name = name;
    this.age = age; 
    // 当第一次调用构造函数时,run()方法不存在,然后执行初始化原型;
    // 当第二次调用,就不会初始化,并且第二次创建新对象,原型也不会载初始化;
    // 这样既得到了封装,又实现了原型方法共享,并且属性都保持独立;
    if(typeof this.run != 'function'){      // 仅在第一次调用时初始化;
      Box.prototype.run = function (){
        return this.name+this.age+'运行中...';
      };
    }
  };
  var box = new Box('lee',10);
  console.log(box.run());
// PS:使用动态原型模式,要注意一点,不可以再使用字面量的方式重写原型,因为会切断实例和新原型之间的联系;
Copy after login

9. Parasitic constructor

// 寄生构造函数,其实就是工厂模式+构造模式;这种模式比较通用,但不能确定对象关系;
  function Box(name,age){
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.run = function (){
      return this.name+this.age+'运行中...';
    };
    return obj;
  }
Copy after login

Three Inheritance
1. Prototype chain

// 继承是面向对象中一个比较核心的概念;
// 其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承;
// 而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成;
// 实质:利用原型让一个引用类型继承另一个引用类型的属性和方法;
  // 原型继承链:Box ==>> Desk ==>> Table;
  function Box(){                 // Box构造;
    this.name = 'Lee';
  }
  function Desk(){                // Desk构造;
    this.age = 100;
  }
  Desk.prototype = new Box();           // 通过创建Box实例,并赋值给Desk.prototype实现的;通过原型,形成链条;  
                          // 实质是:重写了Desk的原型对象,取而代之的是一个新类型Box的实例;
                          // 也就是说原来存在于Box实例中的属性和方法,现在也存在与Desk.prototype中了;
  var desk = new Desk();
  console.log(desk.age);             // 100;
  console.log(desk.name);             // =>Lee;

  function Table(){
    this.level = 'AAA';
  }
  Table.prototype = new Desk();          // 继续原型链继承;Table继承了Desk;
  var table = new Table();
  console.log(table.name);            // Lee;
Copy after login

2. The relationship between prototype and instance;

// PS:以上原型链继承缺少一环,那就是Object,所有的构造函数都继承自Object;
// 而继承Object是自动完成的,并不需要手动继承;
  console.log(table instanceof Object);       // =>true;
  console.log(desk instanceof Table);        // =>false;Desk是Table的超类;
  console.log(table instanceof Desk);        // =>true;
  console.log(table instanceof Box);         // =>true;
// 在JS中,被继承的函数称为超类型(父类,基类);
// 继承的函数称为子类型(子类,派生类);
// 继承问题:
// 字面量重写原型会中断关系;
// 子类型无法给超类型传递参数;
Copy after login

3. Borrowing constructor (object impersonation)
//In order to solve the problem of reference sharing and the inability to pass parameters to super types;

// 在子类型构造函数的内部调用超类型构造函数;
  function Box(age){
    this.name = ['Lee','Jack','Hello'];
    this.age = age;
  }
  function Desk(age){
    // 继承了Box;同时还传递了参数;
    // 这样一来,就会在新Desk对象上执行Box()函数中定义的所有对象初始化代码;
    Box.call(this,age);              // 对象冒充,Desk继承Box,并可以给超类型传参;
    // 为了确保Box构造函数不会重写子类型的属性,可以在超类型构造函数后,再添加应该在子类型中定义的属性;
    this.height = 175;

  }
  var desk = new Desk(200);               // 向Desk()函数传参,再通过函数冒用向Box()函数传参;
  console.log(desk.age);               // =>200;
  console.log(desk.name);              // =>['Lee','Jack','Hello'];
  desk.name.push('AAA');               // =>添加的新数据,只添加给desk;
  console.log(desk.name);              // =>['Lee','Jack','Hello','AAA'];
Copy after login

4. Combined inheritance (prototype chain, borrowed constructor)
// Although borrowing constructors solves the problem of reference sharing and the inability to pass parameters to super types, it does not use prototypes and reuse is impossible; therefore, the combined inheritance model is needed;

// 使用原型链实现对原型属性和方法的继承;
// 通过借用构造函数来实现对实例属性的继承;
// 这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有他自己的属性;
  function Box(age){                    // 构造函数;
    this.name = ['Lee','Jack','Hello'];
    this.age = age;
  }
  Box.prototype.run = function(){            // 原型;
    return this.name+this.age;
  }
  function Desk(age){
    Box.call(this,age);              // 继承属性; 对象冒充; 将Box对象的作用域扩充到Desk中,Desk就会继承Box里的属性和方法;
  }                              
  Desk.prototype = new Box();            // 继承方法; 原型链继承;
  var desk = new Desk(100);
  console.log(desk.run());              // =>Lee,Jack,Hello100
// 最常用的继承模式;
Copy after login

5. Prototypal inheritance?

// 这种继承借助原型并基于已有的对象创建对象,同时还不必因此创建自定义类型;
  function obj(o){                // 传递一个字面量函数;
    function F(){};               // 创建一个构造函数;
    F.prototype = o;              // 把字面量函数赋值给构造函数的原型;
    return new F();               // 返回实例化的构造函数;
  }
  var box = {                   // 字面量对象;
    name:'Lee',
    arr:['brother','sisiter']
  };
  var box1 = obj(box);
  console.log(box1.name);             // =>Lee;
  box1.name = 'Jack';
  console.log(box1.name);             // =>Jack;

  console.log(box1.arr);             // =>brother,sister;
  box1.arr.push('father');            // 
  console.log(box1.arr);             // =>brother,sister,father;

  var box2 = obj(box);
  console.log(box2.name);             // =>Lee;
  console.log(box2.arr);             // =>brother,sister,father;引用类型共享了;
Copy after login

6. Parasitic inheritance?

// 把原型式+工厂模式结合而来,目的是为了封装创建对象的过程;
// 创建一个仅用于封装继承过程的函数,
  function create(o){               // 封装创建过程;
    var f = obj(o);
    f.run = function(){
      return this.arr;            // 同样会共享引用;
    };
    return f;
  }
Copy after login

7. Parasitic combined inheritance?

// 之前说过,组合式继承是JS最常用的继承模式;
// 但是,组合式继承也有问题:
// 超类型在使用过程中会被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部;
  function Box(name){
    this.name = name;
    this.arr = ['brother','sister'];
  }
  Box.prototype.run = function(){
    return this.name;
  }
  function Desk(name,age){
    Box.call(this,name);            // 第二次调用Box;
    this.age = age;
  }
  Desk.prototype = new Box();           // 第一次调用Box;

// 寄生组合式继承:
// 通过借用构造函数来继承属性,
// 通过原型链的混成形式来继承方法;
// 解决了两次调用的问题;
  function obj(o){
    function F(){};
    F.prototype = o;
    return new F();
  }
  function create(box,desk){
    var f = obj(box.prototype);
    f.constructor = desk;
    desk.prototype = f;
  }
  function Box(name){
    this.name = name;
    this.arr = ['brother','sister'];
  }
  Box.prototype.run = function(){
    return this.name;
  }
  function Desk(name,age){
    Box.call(this,name);
    this.age = age;
  }
  inheritPrototype(Box,Desk);            // 通过这里实现继承;

  var desk = new Desk('Lee',100);
  desk.arr.push('father');
  console.log(desk.arr);
  console.log(desk.run());

  var desk2 = new Desk('Jack',200);
  console.log(desk2.arr);              // 两次引用问题解决;
Copy after login

Four Summary


1. Create object

Objects can be created and enhanced during code execution and are therefore dynamic rather than strictly defined entities;
In the absence of classes, objects can be created using the following patterns;
(1). Factory pattern: Use simple functions to create objects, add properties and methods to the objects, and then return the objects;
This pattern was later replaced by the constructor pattern;
(2). Constructor mode: You can customize the reference type and use the new operator to create a built-in object instance at a glance;
Disadvantages: Each of its members cannot be reused, including functions; since functions can not be limited to any object, there is no reason not to share functions among multiple objects;
(3). Prototype mode: Use the prototype attribute of the function to specify those attributes and methods that should be shared;
When using the constructor pattern and the prototype pattern in combination, use the constructor to define instance properties and use the prototype to define shared properties and methods;

2. Prototype chain

The construction of the prototype chain is achieved by assigning an instance of a type to the prototype of another constructor;
Subtypes can access all properties and methods of the supertype;
The problem with the prototype chain is that object instances share all inherited properties and methods, so they are not suitable for use alone;
Solution: Borrow the constructor, that is, call the supertype constructor inside the subtype constructor;
In this way, each instance can have its own properties, and it can also ensure that only the constructor is used to define the type;
The most used inheritance pattern is compositional inheritance; it uses the prototype chain to inherit shared properties and methods, and inherits instance properties by borrowing constructors;

3. Inheritance mode

(1). Prototypal inheritance: Inheritance can be achieved without having to define a constructor in advance; its essence is to perform a shallow copy of a given object;
The copied copy can be further transformed;
(2). Parasitic inheritance: Create an object based on an object or some information, then enhance the object, and finally return the object;
In order to solve the inefficiency problem caused by the combined inheritance pattern due to multiple calls to the supertype constructor, this pattern can be used together with combined inheritance;
(3). Parasitic combined inheritance: It combines the advantages of parasitic inheritance and combined inheritance, and is the most effective way to implement type-based inheritance;

Related labels:
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template