這篇要介紹的是用 instanceof 這個函式,將會沿用上一篇的範例進行解說。
instanceof 這個運算子用於檢測某個建構函式的 prototype 是否出現在某個實體物件的原型鏈上。
語法如下:
object instanceof constructor
讀者可以先閱讀以下程式碼,這個範例跟上一篇比起來,差異在最後面增加了幾行的程式碼,其中使用到了 instanceof。
function Shape(x, y) {
this.x = x;
this.y = y;
}
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
};
function Rectangle(...args) {
Shape.apply(this, args);
}
const properties = {
name: {
value: "長方形",
enumerable: true,
writable: true,
},
getPos: {
value: function () {
console.log(`Current Position: ${this.x}, ${this.y}.`);
},
},
};
Rectangle.prototype = Object.create(Shape.prototype, properties);
Rectangle.prototype.constructor = Rectangle;
const rect = new Rectangle(0, 0);
console.log('Is rect an instance of Rectangle?', rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?', rect instanceof Shape); // true
前面說到 instanceof 這個運算子用于檢測某個建構函式的 prototype 是否出現在某個實體物件的原型鏈上,所以觀察程式碼後,會發現 rect instanceof Rectangle
或是 rect instanceof Shape
都是 true。
對照圖來看,的確 Rectangle 和 Shape 的 prototype 都在原型鏈上。
因為使用 typeof 去判斷物件型別時蠻多情況都會印出 object,所以改成用 instanceof,可以更加細分例如是陣列、物件、Date 物件...等。
console.log(typeof 123); // 'number'
console.log(typeof Infinity); // 'number'
console.log(typeof NaN); // 'number'
console.log(typeof '123'); // 'string'
console.log(typeof true); // 'boolean'
console.log(typeof false); // 'boolean'
console.log(typeof Symbol()); // 'symbol'
console.log(typeof Object); // 'function',因為實際上這個 Object 是 Object() constructor
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/Object
console.log(typeof Object.prototype); // 'object'
console.log(typeof null); // 'object',null 因為 JS 早期設計的問題,所以也被判定為 null
console.log(typeof [1, 2, 3]); // 'object'
console.log(typeof { name: 'harry' }); // 'object'
console.log(typeof new Date); // 'object'
console.log(typeof function() {}); // 'function'
console.log([1, 2, 3] instanceof Array); // true
console.log({ name: 'harry' } instanceof Object); // true
console.log(new Date() instanceof Date); // true
console.log(new String() instanceof String); // true
console.log(function() {} instanceof Function); // true
// 這裡需要注意的是陣列、函式、Date 物件等用 `instanceof Object` 判定也會是 true,可以去思考原型鏈就能理解
console.log([1, 2, 3] instanceof Object); // true
console.log(new Date() instanceof Object); // true
console.log(function() {} instanceof Object); // true
Array.isArray()
可以判斷是否一個變數值是一個陣列Object.prototype.toString()
也能判斷物件型別是哪陣列、函式還是物件Object.prototype.toString({}); // "[object Object]"
Object.prototype.toString.call({}); // 同上结果,加上call也ok
Object.prototype.toString.call(1); // "[object Number]"
Object.prototype.toString.call('1'); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(function () {}); // "[object Function]"
Object.prototype.toString.call(null); //"[object Null]"
Object.prototype.toString.call(undefined); //"[object Undefined]"
Object.prototype.toString.call(/123/g); //"[object RegExp]"
Object.prototype.toString.call(new Date()); //"[object Date]"
Object.prototype.toString.call([]); //"[object Array]"
Object.prototype.toString.call(document); //"[object HTMLDocument]"
Object.prototype.toString.call(window); //"[object Window]"
最後,我們來嘗試著自己實作一個 instanceof。
實作的觀念就是根據原型鏈一直去找指定物件(第一個參數)的 __proto__
,如果有找到,就回傳 true,原型鏈都找完還是沒找到則回傳 false。
function myInstanceof(obj, target) {
while (true) {
if (obj === null || typeof obj !== 'object') return false;
if (obj === target.prototype) return true;
obj = obj.__proto__;
}
}
我們可以將前面範例的 rect 物件和 Rectangle、Shape 函式拿來驗證:
console.log(myInstanceof(rect, Rectangle) );
// true,因為 rect.__proto__ === Rectangle.prototype
console.log(myInstanceof(rect, Shape) );
// true,因為 rect.__proto__.__proto__ === Rectangle.prototype
console.log(myInstanceof(rect, String) );
// false,原型鏈找不到 String.prototype
原型繼承的系列文就到這邊結束了,明天將會進入重要的非同步處理的系列。