JavaScript new 操做符 OOP(一) JavaScript 原型鏈 OOP(二)

 

什麼是對象

   對象是單個實物的抽象,一般須要一個模板,表示某一類實物的共同特徵,而後對象根據這個模板生成。
對象是一個容器,封裝了屬性(property)和方法(method),屬性是對象的狀態,方法是對象的行爲(完成某種任務)。
好比,咱們能夠把動物抽象爲animal對象,使用「屬性」記錄具體是那一種動物,使用「方法」表示動物的某種行爲(奔跑、捕獵、休息等等)。

面向對象編程的第一步,就是要生成對象。

 典型的面向對象編程語言(好比 C++ 和 Java),都有「類」(class)這個概念。所謂「類」就是對象的模板,對象就是「類」的實例。

JavaScript 語言的對象體系,不是基於「類」的,而是基於構造數`constructor`和原型鏈`prototype`, 因此JS  專門使用構造函數做爲對象模板  ,一個構造函數,可生成多個實列對象,它們有相同的結構html

 

構造函數與普通函數區別

- 構造函數就是一個普通的函數,可是有本身的特徵和用法。
- 函數體內部使用了this關鍵字,表明了所要生成的對象實例。
- 生成對象的時候,必須使用new命令。
//constructor
var Bird = function () {
  this.name = 'lai fu';
};
var bird1 = new Bird(); // 也可使用 new Bird; 推薦使用前者
console.log(bird1.name) // "lai fu"

//ordinary
var a =Bird();
console.log(a) // undefined
console.log(a.name) // typeError
name // 'laifu'

 

 防止把構造函數`constructor`當普通函數使用

//使用 嚴格模式
function Fubar(foo, bar){
  'use strict';
    this._foo = foo;
    this._bar = bar;
}
Fubar()// TypeError
//判斷 this 不是構造函數(constructor)的實列對象 那麼手動返回自身constructor

function Far(a){
    if (!(this instanceof Far)) return new Far(a);
 
    this._a=a;
}
Far(1)._a

 

`new`命令的原理

- 建立一個空對象,做爲將要返回的對象實例。
- 將這個空對象的原型,指向構造函數的prototype屬性。
- 將這個空對象賦值給函數內部的this關鍵字。
- 開始執行構造函數內部的代碼。
/**
*新生成一個空對象
*連接到原型
*綁定 this
*返回新對象
**/
function _new(constuctor,param) {
 
  // 得到構造函數
  let Con = [].shift.call(arguments);
  // 連接到原型
  let obj = Object.create(Con.prototype);
  // 綁定 this,執行構造函數
  let result = Con.apply(obj, arguments)
  // 確保 new 出來的是個對象
  return (typeof(result) === 'object' && result != null) ? result : obj
}
var fn = _new(
  function Person (name,age){
  this.name = name; this.age = age
  }, 'Owen', 28);
fn.name // 'Owen'

 

new.target

 
function f() {
    console.log(new.target === f);
}


f() // false
new f() // true
//可利用 它來判斷是否使用 new 命令


function f() {
    if (!new.target) {
    throw new Error('請使用 new 命令調用!');
}
f() // Uncaught Error: 請使用 new 命令調用!

 

`this`實質

-  原始的對象以字典結構保存,每個屬性名都對應一個屬性描述對象。編程

JavaScript 存儲變量其實是如下面的形式保存的。
var obj = { name: "Owen" };
{
    name: {
        [[value]]: "Owen" //函數的地址
        [[writable]]: true //是否可賦值
        [[enumerable]]: true//是否可枚舉
        [[configurable]]: true//是否可配置
    }
}
//屬性的值保存在屬性描述對象的value屬性裏面。         

  
若是 a 屬性的值是 引用值  那麼屬性將如下面的形式保存的:數組

var obj = { fn: function () {} };
/*
{
    fn: {
        [[value]]:
        [[writable]]: true
        [[enumerable]]: true
        [[configurable]]: true
      }
}
*/

 

因爲函數是一個單獨的值,因此它能夠在不一樣的環境(上下文)執行。
var f n= function () {};
var obj = { f: fn };

// 單獨執行
fn()

// obj 環境執行
obj.f()
 JavaScript 容許在函數體內部,引用當前環境的其餘變量。
因爲函數能夠在不一樣的運行環境執行,因此須要有一種機制,可以在函數體內部得到當前的運行環境(context)。
因此,this就出現了,它的設計目的就是在函數體內部,指代函數當前的運行環境。
 
下面這幾種用法,都會改變this的指向。
(obj.fn = obj.fn)() // window
// 等同於
(function () {
console.log(this);
})()

(false || obj.fn)() // window
// 等同於
(false || function () {
console.log(this);
})()

(4, obj.fn)() // window
// 等同於
(4, function () {
console.log(this);
})()

 

數組調用forEach 方法時函數內部this 指向window ,將父級上下文傳遞給forEach 改變this指向
 
var o = {
        v: 'hello',
        p: [ 'Owen', 18 ],
        f: function f() {
                this.p.forEach(function (item) {
                console.log(this.v + '-' + item);
            }, this); //將外層的this傳遞給forEach方法
        }
    }

o.f() // hello-Owen hello-18                

 JavaScript 原型鏈 OOP(二)app

end
相關文章
相關標籤/搜索