Kemahiran berorientasikan objek JavaScript dan prototaip_javascript

WBOY
Lepaskan: 2016-05-16 16:04:44
asal
1160 orang telah melayarinya

ECMAScript mempunyai dua model pembangunan: 1. Berfungsi (prosedur); 2. Berorientasikan objek (OOP);

1 Cipta objek

1. Penciptaan biasa objek

// 创建一个对象,然后给这个对象新的属性和方法;
  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());       // 输入属性和方法的值;
// 缺点:想创建类似的对象,就会产生大量的代码;
Salin selepas log masuk
2. Cipta objek dalam mod kilang


// 这种方法就是为了解决实例化对象产生大量代码重复的问题;
  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;
Salin selepas log masuk
3. Pembina mencipta objek


// 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;
// 使用构造函数,即解决了重复实例化的问题,有解决了对象识别的问题;
Salin selepas log masuk

Perbezaan antara menggunakan corak pembina dan kilang:
(1) Kaedah pembina tidak memaparkan objek yang dicipta (Objek baharu);
(2) Tetapkan sifat dan kaedah kepada objek ini; (3). No return statement;1 //Spesifikasi konstruktor:
(1). Nama fungsi (Kotak fungsi) dan nama binaan instantiasi (Kotak baharu) adalah sama dan menggunakan huruf besar
(2). Untuk mencipta objek contoh melalui pembina, anda mesti menggunakan operator baharu;

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

  var o = new Object();
  Box.call(o,'jack',200);           // 对象冒充调用;
  // 将Box对象作用域扩充到对象o;Box()方法的运行环境已经变成了对象o里;
Salin selepas log masuk
Masalah dengan pembina:

Apabila menggunakan pembina untuk mencipta setiap tika, kaedah dalam pembina mesti dicipta semula pada setiap tika; Oleh kerana fungsi dalam ECMAScript ialah objek, setiap kali fungsi ditakrifkan, objek dijadikan instantiated Mencipta fungsi dengan cara ini menghasilkan rantai skop dan resolusi pengecam yang berbeza;

Dua Prototaip

//Setiap fungsi yang kami cipta mempunyai atribut prototaip, iaitu objek;

// Tujuan: Mengandungi sifat dan kaedah yang boleh dikongsi oleh semua keadaan jenis tertentu
// Pemahaman: prototaip ialah objek prototaip objek yang dibuat dengan memanggil pembina;
// Kelebihan menggunakan prototaip ialah semua kejadian objek boleh berkongsi sifat dan kaedah yang terkandung di dalamnya;

// Dalam erti kata lain, anda tidak perlu mentakrifkan maklumat objek (sifat/kaedah) dalam pembina, tetapi anda boleh terus menambah maklumat ini pada prototaip

1. Mod prototaip (prototaip menambah atribut dan kaedah)


2. Prototaip dan dalam pengendali
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; 原型里原来的值;
Salin selepas log masuk

Bagaimana untuk menentukan sama ada harta itu dalam contoh pembina atau dalam prototaip Anda boleh menggunakan fungsi hasOwnProperty() untuk mengesahkan; console.log(box.hasOwnProperty('name')); // Jika ada contoh, kembalikan benar, jika tidak, kembalikan palsu; Operator dalam akan kembali benar apabila harta yang diberikan boleh diakses melalui objek, sama ada harta itu wujud dalam contoh atau prototaip; console.log('nama' dalam kotak); // =>benar, wujud dalam contoh atau prototaip;

3. Sintaks prototaip yang lebih ringkas (mod literal prototaip)




4. Sifat dinamik prototaip (penulisan semula akan menimpa kandungan sebelumnya)


  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;
Salin selepas log masuk
5. Prototaip objek asli


// Objek prototaip bukan sahaja boleh digunakan dalam objek tersuai, tetapi juga jenis rujukan terbina dalam ECMAScript boleh digunakan dengan cara ini,
// Dan jenis rujukan terbina dalam itu sendiri juga menggunakan prototaip;

console.log(Array.prototype.sort); // =>function sort() { [kod asli] };
console.log(String.prototype.substring); // =>substring fungsi() { [kod asli] };
// 原型的声明是有先后顺序的,所以,重写的原型会切断之前的原型;
  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运行中...;
  // 重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;对象实例引用的仍然是最初的原型;
Salin selepas log masuk

6. Masalah dengan objek prototaip




7 Gunakan gabungan corak pembina (data tidak dikongsi oleh objek) dan corak prototaip (data dikongsi oleh objek)


// 原型模式创建对象缺点:省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的;
// 而原型最大的有点就是共享,属性共享;
// 但是,如果原型中的属性包含引用类型(对象),共享就会存在一定问题;
  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;
  // 数据共享导致实例化出的数据不能保存自己的特性;
Salin selepas log masuk
8. Mod prototaip dinamik (merangkumkan prototaip ke dalam pembina)


// 为了解决构造传参和共享问题,组合构造函数+原型模式:
  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:这种混合模式很好的解决了传参和引用共享的大难题;是创建对象比较好的方法;
Salin selepas log masuk
9. Pembina parasit


// 原型模式,不管是否调用了原型中的共享方法,它都会初始化原型中的方法;
// 并且在声明一个对象时,构造函数+原型让人感觉怪异;最好把构造函数和原型封装到一起;
  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:使用动态原型模式,要注意一点,不可以再使用字面量的方式重写原型,因为会切断实例和新原型之间的联系;
Salin selepas log masuk
Tiga Pusaka

1. Rantaian prototaip

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



3. Meminjam pembina (penyamaran objek)
//Untuk menyelesaikan masalah perkongsian rujukan dan ketidakupayaan untuk menghantar parameter kepada jenis super;
// 继承是面向对象中一个比较核心的概念;
// 其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承;
// 而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;
Salin selepas log masuk


4. Warisan gabungan (rantai prototaip, pembina yang dipinjam)
// Walaupun pembina pinjaman menyelesaikan masalah perkongsian rujukan dan ketidakupayaan untuk menghantar parameter kepada jenis super, ia tidak menggunakan prototaip dan penggunaan semula adalah mustahil, model warisan gabungan diperlukan;
// 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中,被继承的函数称为超类型(父类,基类);
// 继承的函数称为子类型(子类,派生类);
// 继承问题:
// 字面量重写原型会中断关系;
// 子类型无法给超类型传递参数;
Salin selepas log masuk


5. Warisan prototaip?

// 在子类型构造函数的内部调用超类型构造函数;
  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'];
Salin selepas log masuk


6. Warisan parasit?

// 使用原型链实现对原型属性和方法的继承;
// 通过借用构造函数来实现对实例属性的继承;
// 这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有他自己的属性;
  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
// 最常用的继承模式;
Salin selepas log masuk


7. Warisan gabungan parasit?

// 这种继承借助原型并基于已有的对象创建对象,同时还不必因此创建自定义类型;
  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;引用类型共享了;
Salin selepas log masuk

Empat Ringkasan


1. Cipta objek

Objek boleh dibuat dan dipertingkatkan semasa pelaksanaan kod dan oleh itu bersifat dinamik dan bukannya entiti yang ditakrifkan secara ketat; Sekiranya tiada kelas, objek boleh dibuat menggunakan corak berikut;
(1). Corak kilang: Gunakan fungsi mudah untuk mencipta objek, menambah sifat dan kaedah pada objek, dan kemudian mengembalikan objek; Corak ini kemudiannya digantikan dengan corak pembina;
(2). Mod Pembina: Anda boleh menyesuaikan jenis rujukan dan menggunakan operator baharu untuk mencipta contoh objek terbina dalam sepintas lalu; Kelemahan: Setiap ahlinya tidak boleh digunakan semula, termasuk fungsi kerana fungsi tidak boleh dihadkan kepada mana-mana objek, tiada sebab untuk tidak berkongsi fungsi antara berbilang objek; (3). Mod prototaip: Gunakan atribut prototaip fungsi untuk menentukan atribut dan kaedah yang harus dikongsi
Apabila menggunakan corak pembina dan corak prototaip dalam kombinasi, gunakan pembina untuk menentukan sifat contoh dan gunakan prototaip untuk menentukan sifat dan kaedah yang dikongsi;



2. Rantaian prototaip

Pembinaan rantai prototaip dicapai dengan memberikan contoh jenis kepada prototaip pembina lain Subjenis boleh mengakses semua sifat dan kaedah superjenis; Masalah dengan rantai prototaip ialah kejadian objek berkongsi semua sifat dan kaedah yang diwarisi, jadi ia tidak sesuai untuk digunakan sahaja;

Penyelesaian: Pinjam pembina, iaitu, panggil pembina supertype di dalam pembina subjenis; Dengan cara ini, setiap contoh boleh mempunyai sifatnya sendiri, dan ia juga boleh memastikan bahawa hanya pembina digunakan untuk menentukan jenis;

Corak pewarisan yang paling banyak digunakan ialah pewarisan komposisi; ia menggunakan rantaian prototaip untuk mewarisi sifat dan kaedah yang dikongsi, dan mewarisi sifat contoh dengan meminjam pembina;


3. Mod warisan

(1). Warisan prototaip: Warisan boleh dicapai tanpa perlu mentakrifkan pembina terlebih dahulu; Salinan yang disalin boleh diubah lagi;

(2). Warisan parasit: Buat objek berdasarkan objek atau beberapa maklumat, kemudian tingkatkan objek, dan akhirnya kembalikan objek itu; Untuk menyelesaikan masalah ketidakcekapan yang disebabkan oleh corak pewarisan gabungan disebabkan oleh berbilang panggilan kepada pembina supertype, corak ini boleh digunakan bersama dengan pewarisan gabungan; (3). Warisan gabungan parasit: Ia menggabungkan kelebihan warisan parasit dan warisan gabungan, dan merupakan cara paling berkesan untuk melaksanakan warisan berasaskan jenis;

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan