首頁  >  文章  >  web前端  >  JavaScript 物件導向精要(一)

JavaScript 物件導向精要(一)

黄舟
黄舟原創
2017-03-06 14:32:461218瀏覽

資料型別

在JavaScript中,資料型別分為兩類:

原始型別

儲存一些簡單數據,如true, 5等。 JavaScript共有5中原始型別:

  • boolean:布爾,值為true或false

  • number:數字,值為任何整數會浮點數

  • string:字串,值為由單引號或雙引號括出的單一字元或連續字元(JavaScript不區分字元類型)

  • null:空類型,其只有一個值:nulll

  • #undefined:未定義,其只有一個值:undefined

#
var name = "Pomy";
var blog = "http://www.ido321.com";
var age = 22;
alert(typeof blog); //"string"
alert(typeof age); //"number"

原始類型的值是直接保存在變數中,並且可以用typeof 進行偵測。但是typeof對null的偵測是回傳object,而不是回傳null:

//弹出Not null
if(typeof null){
    alert("Not null");   
}else{
    alert("null");
}

所以偵測null時,最好用全等於(===),其還能避免強制型別轉換:

console.log("21" === 21);  //false
console.log("21" == 21);  //true
console.log(undefined == null);  //true
console.log(undefined === null);  //false

對於字串、數字或布林值,其都有對應的方法,這些方法來自於對應的原始封裝類型:String、Number和Boolean。原始封裝類型將會自動建立。

var name = "Pomy";
var char = name.charAt(0);
console.log(char);  //"P"

在JavaScript引擎中發生的事情:

var name = "Pomy";
var temp = new String(name);
var char = temp.charAt(0);
temp = null;
console.log(char);  //"P"

字串物件的引用在用完之後立即被銷毀,所以不能給字串添加屬性,並且instanceof檢測對應類型時均回傳false:

var name = "Pomy";
name.age = 21;
console.log(name.age);   //undefined
console.log(name instanceof String);  //false

引用型別

儲存為對象,實質是指向記憶體位置的引用,所以不在變數中儲存物件。除了自訂的對象,JavaScript提供了6中內建類型:

  • Array:數組類型,以數字為索引的一組值的有序列表

  • Date:日期和時間類型

  • ##Error:運行期間錯誤類型

  • Function:函數類型

  • Object:通用物件類型

  • RegExp:正規表示式類型

可以用new來實例化每一個對象,或用字面量形式來建立對象:

var obj = new Object;
var own = {
            name:"Pomy",
            blog:"http://www.ido321.com",
            "my age":22
        };
console.log(own.blog);    //访问属性
console.log(own["my age"]); 
obj = null;  //解除引用

obj 並不包含對象實例,而是一個指向記憶體中實際對象所在位置的指標(或者說引用)。因為typeof對所有非函數的參考型別均傳回object,所以需要用instanceof來偵測引用型別。

函數

在JavaScript中,函數就是物件。使函數不同於其他物件的決定性特性是函數存在一個被稱為[[Call]]的內部屬性。內部屬性無法透過程式碼存取而是定義了程式碼執行時的行為。

建立形式

1、函數宣告:用function關鍵字,會被提升到上下文

2、函數表達式:不能被提升
3、實例化Function內建置類型

sayHi();    //函数提升
function sayHi(){
    console.log("Hello");
}
//其他等效等效方式
/*
var sayHi = function(){
     console.log("Hello");
}
var sayHi = new Function(" console.log(\"Hello\");");
*/

參數

JavaScript函數的另一個獨特之處在於可以給函數傳遞任意數量的參數。函數參數被保存在arguments類別數組物件中,其自動存在函數中,能透過數字索引來引用參數,但它不是數組實例:

alert(Array.isArray(arguments));   //false

類別數組物件arguments 保存的是函數的實參,但並不會忽略形參。因而,arguments.length返回實參列表的長度,arguments.callee.length返回形參列表的長度。

function ref(value){
    return value;
}
console.log(ref("Hi"));
console.log(ref("Hi",22));
console.log(ref.length);  //1

函數中的this

關於this的問題,可參考此文:JavaScript中的this。

JavaScript提供了三個方法用來改變this的指向:call、apply和bind。三個函數的第一個參數都是指定this的值,其他參數都作為參數傳遞給函數。

物件

物件是一種參考類型,建立物件常見的兩種方式:Object建構子與物件字面量形式:

var per1 = {
    name:"Pomy",
    blog:"http://www.ido321.com"
};
var per2 = new Object;
per2.name = "不写代码的码农";

屬性運算

在JavaScript中,可以隨時為物件新增屬性:

per1.age = 0;
per1.sayName = function(){
    alert(this.name);   //"Pomy"
}

因而,在偵測物件屬性是否存在時,常犯的一個錯誤是:

//结果是false
if(per1.age){
    alert(true)
}else{
    alert(false);
}

per1.age 是存在的,但是其值是0,所以不能滿足if條件。 if判斷中的值是物件、非空字串、非零數字或true時,判斷會評估為真;而當值為一個null、undefined、0、false、NaN或空字串時評估為假。

因而,偵測屬性是否存在時,有另外的兩種方式:in和hasOwnProperty(),前者會偵測原型屬性和自有(實例)屬性,後者只偵測自有(實例)屬性。

console.log("age" in per1);  //true
console.log(per1.hasOwnProperty("age"));  //true
console.log("toString" in per1);  //true
console.log(per1.hasOwnProperty("toString"));  //false

物件per1並沒有定義toString,該屬性繼承於Object.prototype,所以in和hasOwnProperty()偵測該屬性時出現差異。如果只想判斷一個物件屬性是不是原型,可以利用以下方法:

function isPrototypeProperty(obj,name){
    return name in obj && !obj.hasOwnProperty(name);
}

若要刪除一個屬性,用delete操作符,用於刪除自有屬性,不能刪除原型屬性。

per1.toString = function(){
    console.log("per1对象");
};
console.log(per1.hasOwnProperty("toString"));   //true
per1.toString();   //"per1对象"
delete per1.toString;
console.log(per1.hasOwnProperty("toString"));   //false
console.log(per1.toString());  //[object Object]

有時需要枚舉物件的可枚舉屬性,也有兩種方式:for-in迴圈和Object.keys(),前者依舊會遍歷出原型屬性,後者只回傳自有屬性。所有可列舉屬性的內部屬性[[Enumerable]]的值均為true。

var per3 = {
    name:"Pomy",
    blog:"http://www.ido321.com",
    age:22,
    getAge:function(){
        return this.age;
    }
};

實際上,大部分原生屬性的[[Enumerable]]的值均為false,即該屬性不能列舉。可以透過propertyIsEnumerable()來偵測屬性是否可以列舉:

console.log(per3.propertyIsEnumerable("name"));  //true
var pros = Object.keys(per3);  //返回可枚举属性的名字数组
console.log("length" in pros);  //true
console.log(pros.propertyIsEnumerable("length"));  //false

属性name是自定义的,可枚举;属性length是Array.prototype的内建属性,不可枚举。

属性类型

属性有两种类型:数据属性和访问器属性。二者均具有四个属性特征:

  • 数据属性:[[Enumerable]]、[[Configurable]]、[[Value]]和[[Writable]]

  • 访问器属性:[[Enumerable]]、[[Configurable]]、[[Get]]和[[Set]]

[[Enumerable]] :布尔值,属性是否可枚举,自定义属性默认是true。
[[Configurable]] :布尔值,属性是否可配置(可修改或可删除),自定义属性默认是true。它是不可逆的,即设置成false后,再设置成true会报错。
[[Value]]:保存属性的值。
[[Writable]]:布尔值,属性是否可写,所有属性默认可写。
[[Get]]:获取属性值。
[[Set]]:设置属性值。

ES 5提供了两个方法用于设置这些内部属性:
Object.defineProperty(obj,pro,desc_map) 和 Object.defineProperties(obj,pro_map)。利用这两个方法为per3添加一个属性和创建一个新对象per4:

Object.defineProperty(per3,"sex",{
    value:"male",
    enumerable:false,
    configurable:false, //属性不能删除和修改,该值也不能被设置成true
});
console.log(per3.sex);  //'male'
console.log(per3.propertyIsEnumerable("sex"));  //false
delete per3.sex;    //不能删除
per3.sex = "female"; //不能修改
console.log(per3.sex);  //'male'
Object.defineProperty(per3,"sex",{
    configurable:true, //报错
});
per4 = {};
Object.defineProperties(per4,{
    name:{
        value:"dwqs",
        writable:true
    },
    blog:{
        value:"http://blog.92fenxiang.com"
    },
    Name:{
        get:function(){
            return this.name;
        },
        set:function(value){
            this.name = value;
        },
        enumerable:true,
        configurable:true
    }
});
console.log(per4.name); //dwqs
per4.Name = "Pomy";
console.log(per4.Name); //Pomy

需要注意的是,通过这两种方式来定义新属性时,如果不指定特征值,则默认是false,也不能创建同时具有数据特征和访问器特征的属性。可以通过Object.getOwnPropertyDescriptor()方法来获取属性特征的描述,接受两个参数:对象和属性名。若属性存在,则返回属性描述对象。

var desc = Object.getOwnPropertyDescriptor(per4,"name");
console.log(desc.enumerable); //false
console.log(desc.configurable); //false
console.log(desc.writable); //true

根据属性的属性类型,返回的属性描述对象包含其对应的四个属性特征。

禁止修改对象

对象和属性一样具有指导其行为的内部特征。其中,[[Extensible]]是一个布尔值,指明改对象本身是否可以被修改([[Extensible]]值为true)。创建的对象默认都是可以扩展的,可以随时添加新的属性。
ES5提供了三种方式:

  • Object.preventExtensions(obj):创建不可扩展的obj对象,可以利用Object.isExtensible(obj)来检测obj是否可以扩展。严格模式下给不扩展对象添加属性会报错,非严格模式下则添加失败。

  • Object.seal(obj):封印对象,此时obj的属性变成只读,不能添加、改变或删除属性(所有属性都不可配置),其[[Extensible]]值为false,[[Configurable]]值为false。可以利用Object.isSealed(obj)来检测obj是否被封印。

  • Object.freeze(obj):冻结对象,不能在冻结对象上添加或删除属性,不能改变属性类型,也不能写入任何数据类型。可以利用Object.isFrozen(obj)来检测obj是否被冻结。 注意:冻结对象和封印对象均要在严格模式下使用。

"use strict";
var per5 = {
    name:"Pomy"
};
console.log(Object.isExtensible(per5));   //true
console.log(Object.isSealed(per5));         //false
console.log(Object.isFrozen(per5));       //false
Object.freeze(per5);
console.log(Object.isExtensible(per5));   //false
console.log(Object.isSealed(per5));       //true
console.log(Object.isFrozen(per5));       //true
per5.name="dwqs";
console.log(per5.name);   //"Pomy"
per5.Hi = function(){
    console.log("Hi");
};
console.log("Hi" in per5);  //false
delete per5.name;
console.log(per5.name);  //"Pomy"
var desc = Object.getOwnPropertyDescriptor(per5,"name");
console.log(desc.configurable);  //false
console.log(desc.writable);  //false

注意,禁止修改对象的三个方法只对对象的自有属性有效,对原型对象的属性无效,仍然可以在原型上添加或修改属性。

function Person(name){
    this.name = name;
}
var person1 = new Person("Pomy");
var person2 = new Person("dwqs");
Object.freeze(person1);
Person.prototype.Hi = function(){
    console.log("Hi");
};
person1.Hi();  //"Hi";
person2.Hi();  //"Hi";

以上就是JavaScript 面向对象精要(一) 的内容,更多相关内容请关注PHP中文网(www.php.cn)!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn