JavaScript 對(duì)象基礎(chǔ)總結(jié)

一. 對(duì)象基本操作

1. 對(duì)象基礎(chǔ)

一個(gè)對(duì)象由許多成員組成,每一個(gè)成員都有一個(gè)名字和一個(gè)值。每一個(gè)名字/值對(duì)被逗號(hào)(,)分隔開,并且名字和值之間由冒號(hào)(:)分隔。

2. 創(chuàng)建對(duì)象

2.1 創(chuàng)建對(duì)象常見的方式有以下六種:
  • new 操作符 + Object 創(chuàng)建對(duì)象
var person = new Object();
  • 字面式創(chuàng)建對(duì)象
var person ={
      name: "sidashen"
}
  • 工廠模式
function createPerson(name,age,family) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.family = family;
    o.say = function(){
        alert(this.name);
    }
    return o;
}

var person1 =  createPerson("sidashen",21,["lida","lier","wangwu"]);  
var person2 =  createPerson("shaonianyingxiong",18,["lida","lier","lisi"]);
  • 構(gòu)造函數(shù)模式
function Person(name,age,family) {
    this.name = name;
    this.age = age;
    this.family = family;
    this.say = function(){
        alert(this.name);
    }
}
var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
var person2 = new Person("lisi",21,["lida","lier","lisi"]);
  • 原型模式
function Person() {
  }
 
Person.prototype.name = "lisi";
Person.prototype.age = 21;
Person.prototype.family = ["lida","lier","wangwu"];
Person.prototype.say = function(){
   alert(this.name);
};

console.log(Person.prototype);  

var person1 = new Person(); 
console.log(person1.name); 
  • 混合模式(構(gòu)造函數(shù)模式+原型模式)
function Person(name,age,family){
    this.name = name;
    this.age = age;
    this.family = family;
}

Person.prototype = {
    constructor: Person,
    say: function(){
          alert(this.name);
    }
}

var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
      console.log(person1);
var person2 = new Person("wangwu",21,["lida","lier","lisi"]);
      console.log(person2);
2.2 創(chuàng)建一個(gè)對(duì)象

var obj = {};

3. 向?qū)ο笾刑砑訉傩?/h4>

語法:對(duì)象.屬性名 = 屬性值;

3.1 向obj中添加name屬性
obj.name = "sidashen";
console.log(obj); // object { name = "sidashen"}
3.2 向obj中添加gender屬性
obj.gender = "female";
console.log(obj); // object { gender = "female"}

4. 讀取對(duì)象中的屬性

語法:對(duì)象.屬性名

obj.name; // "sidashen"

注意:如果讀取對(duì)象中沒有的屬性不會(huì)報(bào)錯(cuò),而是返回"undefined".

5. 修改屬性值

語法:對(duì)象.屬性名 = 新值

obj.name = "shaonianyingxiong";
console.log(obj); // object { name = "shaonianyingxiong"}

5. 刪除對(duì)象的屬性

語法:delete 對(duì)象.屬性名

delete obj.name; //刪除name屬性
console.log(obj.name); // "undefined"

二. 屬性名和屬性值

1. 屬性名

1.1 對(duì)象的屬性名不強(qiáng)制要求遵守標(biāo)識(shí)符規(guī)范。

如:obj.var = "hello"; 也可以命名,但不推薦。

1.2 如果使用特殊屬性名,如@#$afj.,不能采用點(diǎn)表示法。
1.3 括號(hào)表示法。

語法:對(duì)象["屬性名"] = 屬性值

obj["123"] = 789; 
console.log(obj["123"]); // 讀取時(shí)也需要采用這種表示法

括號(hào)表示法更靈活,在[]中可以直接傳遞一個(gè)變量,這樣變量值是多少就讀取那個(gè)屬性。

var n = "123";
console.log(obj[n]); // "789"
var n = "nihao";
console.log(obj[n]); // "nihao"

2. 屬性值

JavaScript對(duì)象的屬性值,可以是任意的數(shù)據(jù)類型,如字符串,數(shù)組,函數(shù),甚至可以是一個(gè)對(duì)象。

var obj2 = new Object();
obj2.name = "周杰倫";
// 將obj2設(shè)置為obj的屬性
obj.test = obj2; // test的屬性值為obj2
console.log(obj.test); // name = "周杰倫"
console.log(obj.test.name); // "周杰倫"

3. in運(yùn)算符

通過該運(yùn)算符,可檢查一個(gè)對(duì)象中是否含有指定屬性,有則返回true,沒有返回false。
語法:"屬性名" in 對(duì)象
檢查obj中是否有test2的屬性

console.log("test2" in obj); // false
console.log("test" in obj); // true

三. 基本數(shù)據(jù)類型和引用數(shù)據(jù)類型

1.

基本數(shù)據(jù)類型:String, Number, Boolean, Null, Undefined;
引用數(shù)據(jù)類型:Object.

1.1 基本數(shù)據(jù)類型舉例
var a = 123;
var b = a;
a++;
console.log(a); //124
console.log(b); //123

在這里會(huì)發(fā)現(xiàn),ab相互獨(dú)立,a的改變并不會(huì)影響b的屬性。

1.2 引用數(shù)據(jù)類型舉例
var obj = new Object();
obj.name = "周杰倫";
console.log(obj.name); // "周杰倫"
var obj2 = obj;
console.log(obj2.name); // "周杰倫"
//修改obj的name屬性
obj.name = "Jay";
console.log(obj2.name); // "Jay"

在這里會(huì)發(fā)現(xiàn),當(dāng)改變obj的屬性值時(shí),obj2的屬性值也跟著發(fā)生改變,這是為什么呢?

2. 基本數(shù)據(jù)類型和引用數(shù)據(jù)類型的區(qū)別

2.1 基本數(shù)據(jù)類型的特點(diǎn)
  • JavaScript中的變量都是保存到棧內(nèi)存的;
  • 基本數(shù)據(jù)類型的值直接在棧內(nèi)存中存儲(chǔ);
  • 值與值之間相互獨(dú)立,修改一個(gè)變量不會(huì)影響其他變量。
2.2 引用數(shù)據(jù)類型的特點(diǎn)
  • 引用數(shù)據(jù)類型(對(duì)象)是保存在堆內(nèi)存中的,每創(chuàng)建一個(gè)新對(duì)象,就會(huì)在堆內(nèi)存中開辟一個(gè)新空間,而變量保存的是對(duì)象的內(nèi)存地址(對(duì)象的引用)。如果兩個(gè)變量保存的是同一個(gè)對(duì)象引用,當(dāng)其中一個(gè)通過一個(gè)變量修改屬性時(shí),另一個(gè)也會(huì)受到影響。
  • 如果設(shè)置obj2的屬性值為null:
obj2 = null;
console.log(obj); // object
console.log(obj2); // null

這里不會(huì)像之前一樣同時(shí)變化是因?yàn)椋?dāng)obj2的屬性值為null,相當(dāng)于將obj2里保存的內(nèi)存地址與堆內(nèi)存中對(duì)應(yīng)的空間斷開連接,所以obj不會(huì)被影響。

2.3 基本數(shù)據(jù)類型和引用數(shù)據(jù)類型在比較時(shí)的不同
  • 基本數(shù)據(jù)類型
var c = 10;
var d = 10;
console.log(c == d); // "ture"
  • 引用數(shù)據(jù)類型
var obj3 = new Object();
var obj4 = new Object();
obj3.name = "Chou";
obj4.name = "Chou";
console.log(obj3 == obj4); // "false"

當(dāng)比較兩個(gè)基本數(shù)據(jù)類型的值時(shí),就是比較值,如 1 ===1;
而比較兩個(gè)引用數(shù)據(jù)類型時(shí),比較的是對(duì)象的內(nèi)存地址,如果兩個(gè)對(duì)象一模一樣,但是地址不同,結(jié)果也返回false。所以在上面例子中,obj3obj4在堆內(nèi)存中分別開辟了新的內(nèi)存空間,有不一樣的內(nèi)存地址,所以比較相等的結(jié)果為false。

四. 對(duì)象字面量

1. 使用對(duì)象字面量來創(chuàng)建一個(gè)對(duì)象

var obj = {};
console.log(obj); // "object"
obj.name = "周杰倫";
console.log(obj.name); // "周杰倫"

2. 使用對(duì)象字面量,可以在創(chuàng)建對(duì)象時(shí),直接指定對(duì)象中的屬性

語法:{屬性名:屬性值1,屬性值2,...}

var obj2 = {name: "Jay"}
var obj2 = {name: "Jay", age: 40}
console.log(obj2); // name = "Jay", age = "40"

上述代碼可簡(jiǎn)寫同時(shí)也是常用寫法為:

var obj2 = {
      name: "Jay", 
      age: 40
}
console.log(obj2); // name = "Jay", age = "40"

3. 對(duì)象字面量的屬性名建議不加引號(hào)

4. 屬性名和屬性值是一組一組的名值對(duì)結(jié)構(gòu),名和值之間使用冒號(hào)(:)連接,多個(gè)名值對(duì)之間使用逗號(hào)(,)隔開。如果一個(gè)屬性之后沒有其他屬性了,則不寫逗號(hào)(,)。

五. this關(guān)鍵詞

JavaScript函數(shù)中的this指向并不是在函數(shù)定義的時(shí)候確定的,而是在調(diào)用的時(shí)候確定的。換句話說,函數(shù)的調(diào)用方式?jīng)Q定了this指向。

JavaScript中,普通的函數(shù)調(diào)用方式有三種:直接調(diào)用、方法調(diào)用和new調(diào)用。除此之外,還有一些特殊的調(diào)用方式,比如通過bind()將函數(shù)綁定到對(duì)象之后再進(jìn)行調(diào)用、通過call()apply()進(jìn)行調(diào)用等。而ES6引入了箭頭函數(shù)之后,箭頭函數(shù)調(diào)用時(shí),其this指向又有所不同

1. 直接調(diào)用

直接調(diào)用,就是通過函數(shù)名(...)這種方式調(diào)用。這時(shí)候,函數(shù)內(nèi)部的this指向全局對(duì)象,在瀏覽器中全局對(duì)象是window,在Node中全局對(duì)象是global

// 簡(jiǎn)單兼容瀏覽器和 NodeJs 的全局對(duì)象
const _global=typeof window==="undefined"?global:window;
function test() {
    console.log(this===_global); // true
}
test();    // 直接調(diào)用

這里需要注意的一點(diǎn)是,直接調(diào)用并不是指在全局作用域下進(jìn)行調(diào)用,在任何作用域下,直接通過 函數(shù)名(...) 來對(duì)函數(shù)進(jìn)行調(diào)用的方式,都稱為直接調(diào)用。

(function(_global) {
    // 通過 IIFE 限定作用域
    function test() {
        console.log(this === _global);  // true
    }
    test();     // 非全局作用域下的直接調(diào)用
})(typeof window === "undefined" ? global : window);

2. bind()對(duì)直接調(diào)用的影響

Function.prototype.bind()的作用是將當(dāng)前函數(shù)與指定的對(duì)象綁定,并返回一個(gè)新函數(shù),這個(gè)新函數(shù)無論以什么樣的方式調(diào)用,其this始終指向綁定的對(duì)象。

const obj = {};
function test() {
    console.log(this === obj);
}
const testObj = test.bind(obj);
test(); // false
testObj();  // true

那么bind()干了什么?

const obj = {};
function test() {
    console.log(this === obj);
}
// 自定義的函數(shù),模擬bind()對(duì)this的影響
function myBind(func, target) {
    return function() {
        return func.apply(target, arguments);
    };
}
const testObj = myBind(test, obj);
test();     // false
testObj();  // true

從上面的示例可以看到,首先,通過閉包,保持了target,即綁定的對(duì)象;然后在調(diào)用函數(shù)的時(shí)候,對(duì)原函數(shù)使用了apply方法來指定函數(shù)的this。當(dāng)然原生的bind()實(shí)現(xiàn)可能會(huì)不同,而且更高效。但這個(gè)示例說明了bind()的可行性。

3. call和apply對(duì)this的影響

Function.prototype.apply()Function.prototype.call()這兩方法的第一個(gè)參數(shù)都是指定函數(shù)運(yùn)行時(shí)其中的this指向。

不過使用applycall的時(shí)候仍然需要注意,如果目錄函數(shù)本身是一個(gè)綁定了this對(duì)象的函數(shù),那applycall不會(huì)像預(yù)期那樣執(zhí)行。

const obj = {};
function test() {
    console.log(this === obj);
}
// 綁定到一個(gè)新對(duì)象,而不是 obj
const testObj = test.bind({});
test.apply(obj);    // true
// 期望 this 是 obj,即輸出 true
// 但是因?yàn)閠estObj綁定了不是obj的對(duì)象,所以會(huì)輸出false
testObj.apply(obj); // false

4. 方法調(diào)用

方法調(diào)用是指通過對(duì)象來調(diào)用其方法函數(shù),它是對(duì)象.方法函數(shù)(...) 這樣的調(diào)用形式。這種情況下,函數(shù)中的this指向調(diào)用該方法的對(duì)象。但是,同樣需要注意bind()的影響。

const obj = {
    // 第一種方式,定義對(duì)象的時(shí)候定義其方法
    test() {
        console.log(this === obj);
    }
};
// 第二種方式,對(duì)象定義好之后為其附加一個(gè)方法(函數(shù)表達(dá)式)
obj.test2 = function() {
    console.log(this === obj);
};
// 第三種方式和第二種方式原理相同
// 是對(duì)象定義好之后為其附加一個(gè)方法(函數(shù)定義)
function t() {
    console.log(this === obj);
}
obj.test3 = t;
// 這也是為對(duì)象附加一個(gè)方法函數(shù)
// 但是這個(gè)函數(shù)綁定了一個(gè)不是 obj 的其它對(duì)象
obj.test4 = (function() {
    console.log(this === obj);
}).bind({});
obj.test();     // true
obj.test2();    // true
obj.test3();    // true
// 受 bind() 影響,test4 中的 this 指向不是 obj
obj.test4();    // false

5. 方法中this指向全局對(duì)象的情況

這里說的是方法中而不是方法調(diào)用中。方法中的this指向全局對(duì)象,如果不是因?yàn)?code>bind(),那就一定是因?yàn)椴皇怯玫姆椒ㄕ{(diào)用方式。

const obj = {
    test() {
        console.log(this === obj);
    }
};
const t = obj.test;
t();    // false

t就是objtest方法,但是t()調(diào)用時(shí),其中的this指向了全局。

6. new調(diào)用

在ES6之前,每一個(gè)函數(shù)都可以當(dāng)作是構(gòu)造函數(shù),通過new調(diào)用來產(chǎn)生新的對(duì)象(函數(shù)內(nèi)無特定返回值的情況下)。而ES6改變了這種狀態(tài),雖然class定義的類用typeof運(yùn)算符得到的仍然是"function",但它不能像普通函數(shù)一樣直接調(diào)用;同時(shí),class中定義的方法函數(shù),也不能當(dāng)作構(gòu)造函數(shù)用new來調(diào)用。
而在ES5中,用new調(diào)用一個(gè)構(gòu)造函數(shù),會(huì)創(chuàng)建一個(gè)新對(duì)象,而其中的this就指向這個(gè)新對(duì)象。這沒有什么懸念,因?yàn)?code>new本身就是設(shè)計(jì)來創(chuàng)建新對(duì)象的。

var data = "Hi";    // 全局變量
function AClass(data) {
    this.data = data;
}
var a = new AClass("Hello World");
console.log(a.data);    // Hello World
console.log(data);      // Hi
var b = new AClass("Hello World");
console.log(a === b);   // false

7. 箭頭函數(shù)中的this

箭頭函數(shù)沒有自己的this綁定。箭頭函數(shù)中使用的this,其實(shí)是直接包含它的那個(gè)函數(shù)或函數(shù)表達(dá)式中的this

const obj = {
    test() {
        const arrow = () => {
            // 這里的 this 是 test() 中的 this,
            // 由 test() 的調(diào)用方式?jīng)Q定
            console.log(this === obj);
        };
        arrow();
    },
    getArrow() {
        return () => {
            // 這里的 this 是 getArrow() 中的 this,
            // 由 getArrow() 的調(diào)用方式?jīng)Q定
            console.log(this === obj);
        };
    }
};
obj.test();     // true
const arrow = obj.getArrow();
arrow();        // true

示例中的兩個(gè)this都是由箭頭函數(shù)的直接外層函數(shù)(方法)決定的,而方法函數(shù)中的this是由其調(diào)用方式?jīng)Q定的。上例的調(diào)用方式都是方法調(diào)用,所以this都指向方法調(diào)用的對(duì)象,即obj。
箭頭函數(shù)讓大家在使用閉包的時(shí)候不需要太糾結(jié)this,不需要通過像_this這樣的局部變量來臨時(shí)引用this給閉包函數(shù)使用。來看一段Babel對(duì)箭頭函數(shù)的轉(zhuǎn)譯可能能加深理解。

// ES6
const obj = {
    getArrow() {
        return () => {
            console.log(this === obj);
        };
    }
}    
// ES5,由 Babel 轉(zhuǎn)譯
var obj = {
    getArrow: function getArrow() {
        var _this = this;
        return function () {
            console.log(_this === obj);
        };
    }
};

另外需要注意的是,箭頭函數(shù)不能用new調(diào)用,不能bind()到某個(gè)對(duì)象(雖然bind()方法調(diào)用沒問題,但是不會(huì)產(chǎn)生預(yù)期效果)。不管在什么情況下使用箭頭函數(shù),它本身是沒有綁定this的,它用的是直接外層函數(shù)(即包含它的最近的一層函數(shù)或函數(shù)表達(dá)式)綁定的this。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 函數(shù)和對(duì)象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對(duì)于任何一門語言來說都是核心的概念。通過函數(shù)可以封裝任意多條語句,而且...
    道無虛閱讀 4,944評(píng)論 0 5
  • 第一章 錯(cuò)誤處理: 錯(cuò)誤: 程序運(yùn)行過程中,導(dǎo)致程序無法正常執(zhí)行的現(xiàn)象(即bug) 現(xiàn)象: 程序一旦出錯(cuò),默認(rèn)會(huì)報(bào)...
    fastwe閱讀 1,250評(píng)論 0 1
  • 第3章 基本概念 3.1 語法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡(jiǎn)單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,503評(píng)論 0 21
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,632評(píng)論 1 32
  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,801評(píng)論 0 3

友情鏈接更多精彩內(nèi)容