本內容是《Web前端開發之Javascript視頻》的課件,請配合大師哥《Javascript》視頻課程學習。

對象是ES的的一種複合數據類型,即引用類型;即,對象就是一組屬性與方法的集合;

ES沒有類的概念,所以它的對象也與其他語言中的對象有所不同;

ES把對象定義為:無序屬性的集合,其屬性可以包含基本值、對象或者函數;相當於説對象是一組無序的值,對象的每個屬性或方法都有一個名字,而每個名字都映射到一個值,正因為這樣,可以把ES的對象看作成一個散列表,其中是一組名值對,它們的值可以是數據或函數;

每個對象都是基於一個引用類型創建的,這個引用類型可以原生類型,也可以是自定義的函數;

一.理解對象:

可以創建一個最簡單的自定義對象,就是使用Object,然後再為它添加屬性和方法,如:

var person = new Object();person.name = "wangwei";person.age = 18;person.jog = "Engineer";person.sayName = function(){    alert(this.name);}

在多種場景中,常用對象字面量創建對象,如:

var person = {    name:"wangwei",    age:18,    job:"Engineer",    sayName:function(){        alert(this.name);    }};

對象中的this:

當一個函數作為對象的屬性存在時,並且通過對象調用這個方法,那麼函數中的this就指向調用函數的對象;

this的好處在於,可以更加方便的訪問對象內部成員;

早綁定和晚綁定:

綁定:把對象的成員與對象實例結合在一起的方法。

早綁定:

指在實例化對象之前定義它的屬性和方法,這樣編譯器或解釋程序就能夠提前轉換機器代碼。ES不是強類型語言,所以不支持早綁定;

晚綁定:

編譯器或解釋程序在運行前,不知道對象的類型。使用晚綁定,無需檢查對象的類型,只需檢查對象是否支持屬性和方法即可;ES中的所有變量都採用晚綁定方法;這樣就允許執行大量的對象操作;

屬性訪問錯誤:

屬性訪問並不總是返回或設置一個值,如果訪問一個不存在的屬性並不會報錯,會返回undefined;但如果試圖訪問一個不存在的對象的屬性就會報錯;null和undefined值是沒有屬性的,因此,訪問這兩個值的屬性就會報錯,如:

var book = {};console.log(book.subtitle);  // undefined// console.log(book.subtitle.length);  // 異常// 解決方案var len = book && book.subtitle && book.subtitle.length;console.log(len);  // undefined,不會報錯

有些屬性是隻讀的,不能重新賦值,有一些對象不允許新增屬性,但如果操作這些屬性,也不會報錯,如:

// 內置構造函數的原型是隻讀的// 賦值失敗,但沒有報錯,Object.prototype沒有修改Object.prototype = 0;

這是歷史遺留問題,但在嚴格模式下會拋出異常;

刪除屬性:

delete刪除對象的屬性,但只是斷開屬性和宿主對象的聯繫,而不會去操作屬性中的屬性;

var a = {p:{x:1}};var b = a.p;delete a.p;console.log(b.x);  // 1

刪除的屬性的引用還存在,因此在某些實現中,有可能會造成內存泄漏;因此,在銷燬對象時,要遍歷屬性中的屬性,依次刪除;

delete刪除成功或者沒有任何副作用時,它返回true;或者刪除的不是一個屬性訪問表達式,同樣返回true,如:

var o = {x:1};delete o.x;delete o.x;console.log(delete o.toString);  // 什麼也沒做,toString是繼承來的console.log(delete 1)  // 無意義

delete不能刪除那些可置性為false的屬性,

某些內置對象的屬性是不可配置的,比如通過變量聲明和函數聲明創建的全局對象的屬性;在嚴格模式下,刪除一個不可配置屬性會報一個類型錯誤,在非嚴格模式中,這些操作會返回false,如:

console.log(delete Object.prototype);// 不能刪除,屬性是不可配置的var x = 1;console.log(delete this.x); // 不能刪除function f(){}console.log(delete this.f); // 不能刪除

在非嚴格模式中,刪除全局對象的可配置屬性時,可以省略對全局對象的引用,但在嚴格模式下會報錯,如:

"use strict";this.x = 1;console.log(delete this.x);console.log(delete x);  // 嚴格模式下異常

因此,必須顯式指定對象及其屬性;

雖然Object構造函數或對象字面量都可以用來創建單個對象,但這些方法有個明顯的缺點:使用同一個接口創建很多對象,會產生大量的重複代碼;為解決這個問題,可以使用工廠模式的方式創建對象;

工廠模式:

工廠模式是軟件工程領域一種廣泛使用的設計模式,其抽象了創建具體對象的過程(還有其他設計模式);在ES中無法創建類,所以就發明了一種函數,用該函數來封裝特定接口創建對象的細節,如:

function createPerson(name,age,job){    var o = new Object();    o.name = name;    o.age = age;    o.job = job;    o.sayName = function(){        alert(o.name);    };    return o;}var p1 = createPerson("wangwei",18,"Engineer");var p2 = createPerson("wujing",28,"doctor");alert(p1.name);alert(p2.name);

在工廠函數外定義對象方法,再通過屬性指向該方法;

// 在上面的代碼中改function sayName(){    alert(this.name);}// 在原來的o.sayName = function(){…}改成如下o.sayName = sayName;

構造函數:

可以使用構造函數來創建特定類型的對象,如:Object和Array這種原生構造函數,在運行時會自動出現在執行環境中;

構造函數內能初始化對象,並返回對象;

此外,也可以創建自定義的構造函數,從而自定義對象類型的屬性和方法;使用此種方式的目的:更加類似真正的面向對象創建(類)對象方法,也就是首先創建類;如:

function Person(name,age,job){    this.name = name;    this.age = age;    this.job = job;    this.sayName = function(){        alert(this.name);    };}var p1 = new Person("wangwei",18,"Engineer");var p2 = new Person("wujing",28,"Doctor");alert(p1.name);alert(p2.name);

這裏的Person本身就是函數,只不過可以用來創建對象而已;

要創建實例對象,必須使用new實例化對象;以這種方式調用函數實際上會經歷經下4個步驟:

創建一個新對象;

將構造函數的作用域賦給新對象(因此this就指向了這個新對象)

執行構造函數中的代碼(為這個新對象添加屬性);

返回新對象,即隱式的返回了this;

關於構造函數的返回值:

// 先使用this,再使用ofunction Person(name,age){    var o = {};    o.name = name;    o.age = age;    return o;}var p = new Person("wangwei",18);console.log(p);

但如果返回是一個原始值,如:return 100,此時無任何影響,説明構造函數內返回的一定是一個對象;

在構造函數內還可以使用閉包:

function Person(name,age){    var money = 100;    this.name = name;    this.age = age;    function show(){        money ++;        console.log(money);    }    this.say = show;}var p1 = new Person();p1.say();p1.say();var p2 = new Person();p2.say();

constructor(構造函數)屬性:

實例都有一個constructor(構造函數)屬性,該屬性指向Person;

即:構造函數方式創建的實例有constructor(構造函數)屬性,該屬性指向類函數,如:

alert(p1.constructor == Person);alert(p2.constructor == Person);

對象的constructor屬性最初是用來標識對象類型的。但檢測對象類型,instanceof操作符更可靠;

alert(p1 instanceof Object);alert(p1 instanceof Person);

構造函數的特點:

構造函數與其他函數的唯一區別:就在於調用它們的方式不同;構造函數也是函數,不存在定義構造函數的特殊語法;

任何函數,只要通過new操作符來調用,那它就可以作為構造函數;而任何函數,如果不通過new操作符調用,就是一個普通函數,如:

// 當作構造函數使用var p1 = new Person("wangwei",18,"Engineer");p1.sayName();// 當作普通函數調用Person("wujing",28,"Doctor");window.sayName();// 在另一個對象的作用域中調用var o = new Object();Person.call(o,"Hello",38,"Worker");o.sayName();

構造函數的缺點:

這種方式雖然比較方便好用,但也並非沒有缺點;缺點是:每個方法都要在每個實例上重新創建一遍,如sayName()方法,每個實例擁有的sayName(),但都不是同一個Function實例,如:

alert(p1.sayName == p2.sayName);  // false

在ES中的函數是對象,因此每定義一個函數,也就實例化了一個對象,從邏輯上説,相當於:

this.sayName = new Function("alert(this.name)");

以這種方式創建函數,會導致不同的作用域鏈和標識符解析;但創建Function新實例的機制仍然是相同的;

可以把函數定義在構造函數外部;如:

function sayName(){    alert(this.name);}function Person(name,age,job){    this.name = name;    this.age = age;    this.job = job;    this.sayName = sayName;}// 當作構造函數使用var p1 = new Person("wangwei",18,"Engineer");var p2 = new Person("wangwei",18,"Engineer");alert(p1.sayName == p2.sayName);  // true

Java發生Object無法強轉為某個類型_嚴格模式

Web前端開發之Javascript-零點程序員-王唯