1. 程式人生 > >JavaScript中typeof,instanceof,hasOwnProperty,in的用法和區別

JavaScript中typeof,instanceof,hasOwnProperty,in的用法和區別

一. typeof操作符

typeof操作符用於返回正在使用值的型別。

// 使用原始值
let mNull = null;
let mUndefined = undefined;
let mString = 'mazey';
let mNumber = 123;
let mBoolean = true;
let mFunction = function () {
    return true;
};

// 用建構函式的方式new一個例項
let oString = new String('cherrie');
let oRegExp = new RegExp('^[0-9]+$');
let oFunction = new Function('x', 'y', 'return x + y');

let oObj = {};
let oNew = new Object();

// typeof值
console.log(typeof mNull); // object
console.log(typeof mUndefined); // undefined
console.log(typeof mString); // string
console.log(typeof mNumber); // number
console.log(typeof mBoolean); // boolean
console.log(typeof mFunction); // function
console.log(typeof oString); // object
console.log(typeof oRegExp); // object
console.log(typeof oFunction); // function
console.log(typeof oObj); // object
console.log(typeof oNew); // object

在《JavaScript啟示錄》中new RegExp()介紹會返回function,但是事實上我在chrome控制檯中看到的是object

於是我console.log(new RegExp('^[0-9]+$')),打印出來的是字串/^[0-9]+$/

console.log(new RegExp('^[0-9]+$')); // /^[0-9]+$/
console.log(RegExp); // ƒ RegExp() { [native code] } 原始值
console.log(String); // ƒ String() { [native code] } 原始值
console.log(/^[0-9]+$/); // /^[0-9]+$/
console.log(new RegExp('^[0-9]+$') === /^[0-9]+$/); // false
console.log(RegExp('^[0-9]+$') === /^[0-9]+$/); // false

綜上可以看出現版本RegExpString Number一樣屬於JavaScript的原始值。

Math作為JavaScript中的靜態物件回返回什麼呢?

console.log(typeof Math); // object
console.log(typeof Math.PI); // number
console.log(typeof Math.ceil); // function

所以Math__proto__還是Objecttypeof還能返回物件的屬性和方法的型別。

typeof使用場景

1.判斷某個變數是否已定義

console.log(typeof aaa); // 'undefined'

// 判斷
if (typeof bbb === 'undefined') {
    console.log('變數未定義');
}

2.區分原始值和複雜值(物件值)

因為複雜值往往返回object,當然有個例外就是原始值裡面的null也返回object,然後function作為Object的例項也是複雜值。

// 判斷是否時複雜值(物件值)
function isObject (m) {
    return (typeof m === 'function' || (typeof m === 'object' && m !== null));
}

console.log(isObject(new RegExp('123'))); // true
console.log(isObject('123')); // false
console.log(isObject(String('123'))); // false
console.log(isObject(null)); // false

// 判斷是否是原始值
function isNative (m) {
    return (m === null || (typeof m !== 'object' && typeof m !== 'function'));
}

console.log(isNative(new RegExp('123'))); // false
console.log(isNative('123')); // true
console.log(isNative(String('123'))); // true
console.log(isNative(null)); // true

3.檢測某個變數是否是函式

當使用閉包時判斷是函式後再進行下一步。

function qqq () {
    let a = 0;
    let b = function () {
        a++;
        console.log(a);
    };
    return b;
}

let ccc = qqq();
console.log(typeof ccc); // function
if (typeof ccc === 'function') {
    ccc(); // 1
    ccc(); // 2
    ccc(); // 3
    ccc(); // 4
}

二. instanceof操作符

通過使用instanceof操作符,可以確定一個物件是否是特定建構函式例項,返回truefalse

instanceof只適用於建構函式建立返回的複雜物件例項

任何時間判斷一個物件(複雜值)是否是Object的例項時,它都將返回true,因為所有物件都繼承自Object()建構函式。

let oFather = function () {
    this.firstName = 'mazey';
};
oFather.prototype.lastName = 'qian';

// 例項
let oSon = new oFather();
console.log(oSon instanceof oFather); // true

// 繼承
let nFather = function () {};
nFather.prototype = new oFather();
nFather.construction = nFather;
console.log(nFather.firstName); // undefined
console.log(nFather.prototype.lastName); // qian
console.log(nFather instanceof oFather); // false
console.log(new nFather() instanceof nFather); // true

// 相對於Object來說
console.log('123' instanceof Object); // false
console.log(new String('123') instanceof Object); // true 構造出來的例項
console.log(null instanceof Object); // false

instanceof使用場景

判斷在一個繼承關係中例項是否屬於它的父類。

// 繼承
let oFather = function () {};
let nFather = function () {};
nFather.prototype = new oFather();
nFather.construction = nFather;

let nSon = new nFather();
console.log(nSon instanceof nFather); // true
console.log(nSon instanceof oFather); // true

三. in操作符和hasOwnProperty方法

in操作符可以檢查一個物件的屬性,包括來自原型鏈的屬性,hasOwnProperty()方法可以檢查來自非原型鏈屬性的物件。

例如現在有一個物件let obj = {name: 'mazey'};name是它自身定義的屬性,toString是它從原型鏈上繼承下來的屬性。

let obj = {name: 'mazey'};
console.log('name' in obj); // true
console.log('toString' in obj); // true
console.log('name' in Object); // true
console.log(obj.hasOwnProperty('name')); // true
console.log(obj.hasOwnProperty('toString')); // false
console.log(Object.hasOwnProperty('name')); // true

所以in操作符查詢的範圍更廣一點,可以用hasOwnProperty()判斷是否是物件自身的屬性,而不是通過類似obj.prototype.foo = 'foo';這樣定義的。

hasOwnProperty方法使用場景

在實際專案中經常使用for...in...來遍歷物件中可列舉的屬性,但是for...in...常常把原型obj.prototype.xxx中的屬性也列舉出來,所以在迴圈的時候可以加上hasOwnProperty()方法判斷下。

function obj0 () {
    this.name = 'mazey',
    this.age = '24'
};
obj0.prototype.gender = 'male';
let obj1 = new obj0();

// 列印所有可列舉屬性
for (let key in obj1) {
    console.log(key); // name age gender 從原型鏈上繼承下來的屬性也會被打印出來
}

// 過濾掉原型鏈上的屬性
for (let key in obj1) {
    if (obj1.hasOwnProperty(key)) {
        console.log(key); // name age
    }
}

四. 總結

1.typeof可以判斷使用值的型別,注意null返回object。2.instanceof驗證建構函式構造出來的例項,可以用來判斷一個物件是否屬於一個父類。3.hasOwnProperty方法常常與in操作符搭配使用,用來遍歷一個物件自身的屬性。

五. 相關擴充套件

1.刪除物件的屬性

若想把一個物件的自身屬性完全刪除,要使用delete操作符。

let obj0 = {
    name: 'mazey',
    age: 24
};

// 刪除age屬性
delete obj0.age;
console.log(obj0.hasOwnProperty('age')); // false
console.log('age' in obj0); // false

// 試著刪除原型鏈上的屬性 toString
delete obj0.toString
console.log('toString' in obj0); // true

需要注意的是delete不會刪除原型鏈上的屬性。

2.可列舉

每個物件的屬性都分為可列舉和不可列舉屬性,可以使用propertyIsEnumerable()方法來檢查哪些屬性是可列舉的。

每個物件都有一個propertyIsEnumerable方法。此方法可以確定物件中指定的屬性是否可以被for...in(for...in語句以任意順序遍歷一個物件的可列舉屬性。對於每個不同的屬性,語句都會被執行)迴圈列舉,但是通過原型鏈繼承的屬性除外。如果物件沒有指定的屬性,則此方法返回false

有的資料說只要能被for..in遍歷的屬性就是可列舉的,實際上要排除從原型鏈上繼承下來的屬性,只有自身的屬性是可列舉的。

// 第一個建構函式
function ConFun0 () {
    this.firstName = 'mazey';
}
ConFun0.prototype.firstCom = 'bang';

// 第二個建構函式
function ConFun1 () {
    this.secondName = 'qian';
}
// 繼承第一個
ConFun1.prototype = new ConFun0();
ConFun1.prototype.constructor = ConFun1;

// 例項
let obj = new ConFun1();
obj.girlName = 'cherrie';

// 是否可列舉
console.log(obj.propertyIsEnumerable('constructor')); // false
console.log(obj.propertyIsEnumerable('firstName')); // false
console.log(obj.propertyIsEnumerable('firstCom')); // false
// 通過原型鏈繼承的屬性不是可列舉
console.log(obj.propertyIsEnumerable('secondName')); // true
console.log(obj.propertyIsEnumerable('girlName')); // true

for (let key in obj) {
    console.log(key); // secondName girlName (原型鏈上的屬性也會被打印出來->) firstName constructor firstCom
}

console.log(`---分割線---`);

for (let key in obj) {
    // 過濾掉原型鏈上的屬性
    if (obj.hasOwnProperty(key)) {
        console.log(key); // secondName girlName
    }
}

所以可列舉的屬性一定能被for..in迴圈遍歷出來,但是for...in迴圈遍歷出來的屬性不一定是可列舉的,需排除從原型鏈上繼承下來的屬性,這裡可以通過hasOwnProperty()方法過濾掉不可列舉屬性。