Trong trường hợp cụ thể của tôi:
callback instanceof Function
hoặc là
typeof callback == "function"
nó có quan trọng không, sự khác biệt là gì?
Tài nguyên bổ sung:
JavaScript-Garden typeof vs instanceof
.constructor
tài sản thay thế.
Trong trường hợp cụ thể của tôi:
callback instanceof Function
hoặc là
typeof callback == "function"
nó có quan trọng không, sự khác biệt là gì?
Tài nguyên bổ sung:
JavaScript-Garden typeof vs instanceof
.constructor
tài sản thay thế.
Câu trả lời:
instanceof
cho các loại tùy chỉnh:var ClassFirst = function () {};
var ClassSecond = function () {};
var instance = new ClassFirst();
typeof instance; // object
typeof instance == 'ClassFirst'; // false
instance instanceof Object; // true
instance instanceof ClassFirst; // true
instance instanceof ClassSecond; // false
typeof
cho các loại được xây dựng đơn giản:'example string' instanceof String; // false
typeof 'example string' == 'string'; // true
'example string' instanceof Object; // false
typeof 'example string' == 'object'; // false
true instanceof Boolean; // false
typeof true == 'boolean'; // true
99.99 instanceof Number; // false
typeof 99.99 == 'number'; // true
function() {} instanceof Function; // true
typeof function() {} == 'function'; // true
instanceof
cho các loại được xây dựng phức tạp:/regularexpression/ instanceof RegExp; // true
typeof /regularexpression/; // object
[] instanceof Array; // true
typeof []; //object
{} instanceof Object; // true
typeof {}; // object
Và điều cuối cùng là một chút khó khăn:
typeof null; // object
Use instanceof for complex built in types
- điều này vẫn dễ bị lỗi. Tốt hơn để sử dụng ES5 Array.isArray()
et al. hoặc các miếng chêm được đề nghị.
Cả hai đều giống nhau về chức năng bởi vì cả hai đều trả về thông tin loại, tuy nhiên cá nhân tôi thích instanceof
vì nó so sánh các loại thực tế hơn là các chuỗi. So sánh kiểu ít bị lỗi của con người và về mặt kỹ thuật sẽ nhanh hơn vì nó so sánh các con trỏ trong bộ nhớ thay vì so sánh toàn bộ chuỗi.
Một lý do tốt để sử dụng typeof là nếu biến có thể không được xác định.
alert(typeof undefinedVariable); // alerts the string "undefined"
alert(undefinedVariable instanceof Object); // throws an exception
Một lý do tốt để sử dụng instanceof là nếu biến có thể là null.
var myNullVar = null;
alert(typeof myNullVar ); // alerts the string "object"
alert(myNullVar instanceof Object); // alerts "false"
Vì vậy, thực sự theo ý kiến của tôi, nó sẽ phụ thuộc vào loại dữ liệu bạn có thể kiểm tra.
instanceof
không thể so sánh với các loại nguyên thủy, typeof có thể.
undefined instanceof Object
trả về false và không ném ngoại lệ. Tôi không biết sự thay đổi gần đây như thế nào, nhưng nó làm cho nó instanceof
hấp dẫn hơn.
undefined instanceof Object
không ném một ngoại lệ bởi vì, eh, undefined
được xác định. Hằng số tồn tại trong không gian tên. Khi một biến không tồn tại (ví dụ do lỗi đánh máy), instanceof sẽ đưa ra một ngoại lệ. Mặt khác, sử dụng typeof trên một biến không tồn tại sẽ mang lại "không xác định".
Để làm cho mọi thứ rõ ràng, bạn cần biết hai sự thật:
Object.setPrototypeOf()
phương pháp (ECMAScript 2015) hoặc theo thuộc __proto__
tính (trình duyệt cũ, không dùng nữa). Thay đổi nguyên mẫu của một đối tượng không được khuyến khích, vì vấn đề hiệu suất. Do đó, thể hiện chỉ áp dụng cho các đối tượng. Trong hầu hết các trường hợp, bạn không sử dụng hàm tạo để tạo chuỗi hoặc số. Bạn có thể. Nhưng bạn gần như không bao giờ làm.
Ngoài ra, thể hiện không thể kiểm tra, chính xác hàm tạo nào đã được sử dụng để tạo đối tượng, nhưng sẽ trả về true, ngay cả khi đối tượng được dẫn xuất từ lớp được kiểm tra. Trong hầu hết các trường hợp, đây là hành vi mong muốn, nhưng đôi khi không. Vì vậy, bạn cần phải giữ tâm trí đó.
Một vấn đề khác là phạm vi khác nhau có môi trường thực thi khác nhau. Điều này có nghĩa là chúng có các phần dựng khác nhau (đối tượng toàn cầu khác nhau, các hàm tạo khác nhau, v.v.). Điều này có thể dẫn đến kết quả bất ngờ.
Ví dụ, [] instanceof window.frames[0].Array
sẽ trở lại false
, bởi vì Array.prototype !== window.frames[0].Array
và mảng kế thừa từ trước đây.
Ngoài ra, nó không thể được sử dụng trên giá trị không xác định, vì nó không có nguyên mẫu.
Bây giờ hãy nói về một điều khó khăn. Điều gì nếu bạn sử dụng constructor để tạo ra một kiểu nguyên thủy?
let num = new Number(5);
console.log(num instanceof Number); // print true
console.log(typeof num); // print object
num++; //num is object right now but still can be handled as number
//and after that:
console.log(num instanceof Number); // print false
console.log(typeof num); // print number
Có vẻ như ma thuật. Nhưng nó không phải như vậy. Nó được gọi là quyền anh (gói giá trị nguyên thủy theo đối tượng) và unboxing (trích xuất giá trị nguyên thủy được bọc từ đối tượng). Loại mã như vậy có vẻ là "một chút" mong manh. Tất nhiên bạn chỉ có thể tránh tạo kiểu nguyên thủy với các hàm tạo. Nhưng có một tình huống khác có thể xảy ra, khi quyền anh có thể đánh bạn. Khi bạn sử dụng Function.call () hoặc Function.apply () trên loại nguyên thủy.
function test(){
console.log(typeof this);
}
test.apply(5);
Để tránh điều này, bạn có thể sử dụng chế độ nghiêm ngặt:
function test(){
'use strict';
console.log(typeof this);
}
test.apply(5);
cập nhật: Kể từ ECMAScript 2015, có thêm một loại gọi là Biểu tượng, có loại biểu tượng riêng == "biểu tượng" .
console.log(typeof Symbol());
// expected output: "symbol"
if an object is created by a given constructor
Điều này là không chính xác. o instanceof C
sẽ trả về true nếu o kế thừa từ C.prototype. Bạn đã đề cập một vài điều về điều này sau trong câu trả lời của bạn nhưng nó không rõ ràng lắm.
Tôi đã phát hiện ra một số hành vi thực sự thú vị (đọc là "khủng khiếp") trong Safari 5 và Internet Explorer 9. Tôi đã sử dụng điều này rất thành công trong Chrome và Firefox.
if (typeof this === 'string') {
doStuffWith(this);
}
Sau đó, tôi kiểm tra IE9 và nó không hoạt động. Bất ngờ lớn. Nhưng trong Safari, nó không liên tục! Vì vậy, tôi bắt đầu gỡ lỗi và tôi thấy rằng Internet Explorer luôn quay trở lại false
. Nhưng điều kỳ lạ nhất là Safari có vẻ là làm một số loại tối ưu hóa trong VM JavaScript của nó, nơi nó là true
sự đầu tiên thời gian, nhưng false
mỗi lần bạn nhấn tải lại!
Não tôi gần như nổ tung.
Vì vậy, bây giờ tôi đã giải quyết về điều này:
if (this instanceof String || typeof this === 'string')
doStuffWith(this.toString());
}
Và bây giờ mọi thứ hoạt động tuyệt vời. Lưu ý rằng bạn có thể gọi "a string".toString()
và nó chỉ trả về một bản sao của chuỗi, tức là
"a string".toString() === new String("a string").toString(); // true
Vì vậy, tôi sẽ sử dụng cả hai từ bây giờ.
Khác biệt thực tế quan trọng khác:
// Boolean
var str3 = true ;
alert(str3);
alert(str3 instanceof Boolean); // false: expect true
alert(typeof str3 == "boolean" ); // true
// Number
var str4 = 100 ;
alert(str4);
alert(str4 instanceof Number); // false: expect true
alert(typeof str4 == "number" ); // true
instanceof
trong Javascript có thể không ổn định - Tôi tin rằng các khung chính cố gắng tránh sử dụng nó. Các cửa sổ khác nhau là một trong những cách mà nó có thể phá vỡ - tôi tin rằng hệ thống phân cấp lớp cũng có thể gây nhầm lẫn.
Có nhiều cách tốt hơn để kiểm tra xem một đối tượng có phải là một loại tích hợp nhất định hay không (thường là những gì bạn muốn). Tạo các chức năng tiện ích và sử dụng chúng:
function isFunction(obj) {
return typeof(obj) == "function";
}
function isArray(obj) {
return typeof(obj) == "object"
&& typeof(obj.length) == "number"
&& isFunction(obj.push);
}
Và như thế.
instanceof
sẽ không hoạt động cho người nguyên thủy, ví dụ "foo" instanceof String
sẽ trở lại false
trong khi typeof "foo" == "string"
sẽ trở lại true
.
Mặt khác typeof
có thể sẽ không làm những gì bạn muốn khi nói đến các đối tượng tùy chỉnh (hoặc các lớp, bất cứ điều gì bạn muốn gọi chúng). Ví dụ:
function Dog() {}
var obj = new Dog;
typeof obj == 'Dog' // false, typeof obj is actually "object"
obj instanceof Dog // true, what we want in this case
Thực tế là các hàm là cả hai nguyên hàm 'hàm' và các thể hiện của 'Hàm', có một chút kỳ lạ khi nó không hoạt động như thế đối với các kiểu nguyên thủy khác, vd.
(typeof function(){} == 'function') == (function(){} instanceof Function)
nhưng
(typeof 'foo' == 'string') != ('foo' instanceof String)
Tôi khuyên bạn nên sử dụng nguyên mẫu callback.isFunction()
.
Họ đã tìm ra sự khác biệt và bạn có thể tin tưởng vào lý do của họ.
Tôi đoán các khung công tác JS khác cũng có những thứ như vậy.
instanceOf
Tôi sẽ không làm việc trên các chức năng được xác định trong các cửa sổ khác, tôi tin. Chức năng của họ khác với bạn window.Function
.
Khi kiểm tra một chức năng, người ta phải luôn luôn sử dụng typeof
.
Đây là sự khác biệt:
var f = Object.create(Function);
console.log(f instanceof Function); //=> true
console.log(typeof f === 'function'); //=> false
f(); // throws TypeError: f is not a function
Đây là lý do tại sao người ta không bao giờ phải sử dụng instanceof
để kiểm tra chức năng.
typeof
sai - f
là tất cả những điều sau đây: một Object
(một đối tượng) và một Function
(một chức năng). Ngoại trừ tôi, nó có ý nghĩa hơn để sử dụng instanceof
vì biết rằng đó là một chức năng tôi biết nó cũng là một đối tượng, vì tất cả các chức năng là các đối tượng trong ECMAScript. Điều ngược lại là không đúng - biết từ typeof
đó f
thực sự là một điều object
tôi không biết rằng đó cũng là một chức năng.
length
, name
và call
từ chức năng, nhưng tất cả chúng đều không còn tồn tại. Điều tồi tệ hơn, nó không thể được gọi và TypeError
nói nó : f is not a function
.
Sự khác biệt thực tế đáng kể:
var str = 'hello word';
str instanceof String // false
typeof str === 'string' // true
Đừng hỏi tôi tại sao.
str
là một chuỗi nguyên thủy, không phải là một đối tượng chuỗi. Điều tương tự cũng xảy ra đối với các nguyên thủy số và các nguyên hàm boolean, chúng không phải là các thể hiện của các đối tác "được xây dựng" của chúng, các đối tượng String, Number và Boolean. JavaScript tự động chuyển đổi ba nguyên thủy này thành các đối tượng khi được yêu cầu (chẳng hạn như sử dụng một phương thức trên chuỗi nguyên mẫu của đối tượng). Mặt trái của sự khác biệt thực tế của bạn, instanceof là tốt hơn để kiểm tra các mảng kể từ đó typeof [] == "object" // true
.
Hiệu suất
typeof
nhanh hơn instanceof
trong trường hợp cả hai được áp dụng.
Tùy thuộc vào động cơ của bạn, sự khác biệt hiệu suất có lợi typeof
có thể là khoảng 20% . (Số dặm của bạn có thể thay đổi )
Đây là một bài kiểm tra điểm chuẩn cho Array
:
var subject = new Array();
var iterations = 10000000;
var goBenchmark = function(callback, iterations) {
var start = Date.now();
for (i=0; i < iterations; i++) { var foo = callback(); }
var end = Date.now();
var seconds = parseFloat((end-start)/1000).toFixed(2);
console.log(callback.name+" took: "+ seconds +" seconds.");
return seconds;
}
// Testing instanceof
var iot = goBenchmark(function instanceofTest(){
(subject instanceof Array);
}, iterations);
// Testing typeof
var tot = goBenchmark(function typeofTest(){
(typeof subject == "object");
}, iterations);
var r = new Array(iot,tot).sort();
console.log("Performance ratio is: "+ parseFloat(r[1]/r[0]).toFixed(3));
Kết quả
instanceofTest took: 9.98 seconds.
typeofTest took: 8.33 seconds.
Performance ratio is: 1.198
instanceof
luôn tuân theo chuỗi nguyên mẫu của đối tượng, vì vậy hình phạt hiệu năng sẽ phụ thuộc vào mức độ trong chuỗi nguyên mẫu là bao nhiêu, instanceof
được thử nghiệm chống lại. Vì vậy, đối với chuỗi thừa kế ngắn, hình phạt sẽ thấp hơn (như [] instanceof Array
,, {} instanceof Object
) và lâu dài - lớn hơn. Vì vậy, nếu cả hai obj instanceof SomeClass
và typeof obj !== 'string'
có nghĩa giống nhau theo quan điểm của một số mã giả định của bạn (nếu bạn chỉ thực hiện một bài kiểm tra if
và không switch
thông qua nhiều lớp, v.v.), thì tốt hơn bạn nên chọn thứ hai, thông minh về hiệu suất,
Đây chỉ là kiến thức bổ sung cho tất cả các giải thích khác ở đây - tôi không gợi ý sử dụng .constructor
ở mọi nơi.
TL; DR: Trong các tình huống typeof
không phải là một lựa chọn và khi bạn biết rằng bạn không quan tâm đến chuỗi nguyên mẫu , Object.prototype.constructor
có thể là một lựa chọn khả thi hoặc thậm chí tốt hơn so với instanceof
:
x instanceof Y
x.constructor === Y
Nó đã ở trong tiêu chuẩn kể từ 1.1, vì vậy không phải lo lắng về khả năng tương thích ngược.
Muhammad Umer đã đề cập ngắn gọn điều này trong một bình luận ở đây. Nó hoạt động trên mọi thứ với một nguyên mẫu - vì vậy mọi thứ không null
hoặc undefined
:
// (null).constructor; // TypeError: null has no properties
// (undefined).constructor; // TypeError: undefined has no properties
(1).constructor; // function Number
''.constructor; // function String
([]).constructor; // function Array
(new Uint8Array(0)).constructor; // function Uint8Array
false.constructor; // function Boolean()
true.constructor; // function Boolean()
(Symbol('foo')).constructor; // function Symbol()
// Symbols work, just remember that this is not an actual constructor:
// new Symbol('foo'); //TypeError: Symbol is not a constructor
Array.prototype === window.frames.Array; // false
Array.constructor === window.frames.Array.constructor; // true
Hơn nữa, tùy thuộc vào trường hợp sử dụng của bạn, nó có thể nhanh hơn rất nhiềuinstanceof
(lý do có thể là nó không phải kiểm tra toàn bộ chuỗi nguyên mẫu). Trong trường hợp của tôi, tôi cần một cách nhanh chóng để kiểm tra xem giá trị có phải là một mảng được gõ không:
function isTypedArrayConstructor(obj) {
switch (obj && obj.constructor){
case Uint8Array:
case Float32Array:
case Uint16Array:
case Uint32Array:
case Int32Array:
case Float64Array:
case Int8Array:
case Uint8ClampedArray:
case Int16Array:
return true;
default:
return false;
}
}
function isTypedArrayInstanceOf(obj) {
return obj instanceof Uint8Array ||
obj instanceof Float32Array ||
obj instanceof Uint16Array ||
obj instanceof Uint32Array ||
obj instanceof Int32Array ||
obj instanceof Float64Array ||
obj instanceof Int8Array ||
obj instanceof Uint8ClampedArray ||
obj instanceof Int16Array;
}
https://run.perf.zone/view/isTypedArray-constructor-vs-instanceof-1519140393812
Và kết quả:
Chrome 64.0.3282.167 (64-bit, Windows)
Firefox 59.0b10 (64-bit, Windows)
Vì tò mò, tôi đã làm một tiêu chuẩn đồ chơi nhanh chống lại typeof
; đáng ngạc nhiên là nó không hoạt động kém hơn nhiều và dường như Chrome còn nhanh hơn một chút:
let s = 0,
n = 0;
function typeofSwitch(t) {
switch (typeof t) {
case "string":
return ++s;
case "number":
return ++n;
default:
return 0;
}
}
// note: no test for null or undefined here
function constructorSwitch(t) {
switch (t.constructor) {
case String:
return ++s;
case Number:
return ++n;
default:
return 0;
}
}
let vals = [];
for (let i = 0; i < 1000000; i++) {
vals.push(Math.random() <= 0.5 ? 0 : 'A');
}
https://run.perf.zone/view/typeof-vs-constructor-opes-or-number-1519142623570
LƯU Ý: Thứ tự các chức năng được liệt kê chuyển đổi giữa các hình ảnh!
Chrome 64.0.3282.167 (64-bit, Windows)
Firefox 59.0b10 (64-bit, Windows)
LƯU Ý: Thứ tự các chức năng được liệt kê chuyển đổi giữa các hình ảnh!
var newObj = new Object;//instance of Object
var newProp = "I'm xgqfrms!" //define property
var newFunc = function(name){//define function
var hello ="hello, "+ name +"!";
return hello;
}
newObj.info = newProp;// add property
newObj.func = newFunc;// add function
console.log(newObj.info);// call function
// I'm xgqfrms!
console.log(newObj.func("ET"));// call function
// hello, ET!
console.log(newObj instanceof Object);
//true
console.log(typeof(newObj));
//"object"
Mặc dù instanceof có thể là một chút nhanh hơn sau đó typeof , tôi thích thứ hai vì một ma thuật có thể như vậy:
function Class() {};
Class.prototype = Function;
var funcWannaBe = new Class;
console.log(funcWannaBe instanceof Function); //true
console.log(typeof funcWannaBe === "function"); //false
funcWannaBe(); //Uncaught TypeError: funcWannaBe is not a function
Một trường hợp nữa là bạn chỉ có thể đối chiếu với instanceof
- nó trả về đúng hoặc sai. Với typeof
bạn có thể nhận được loại cung cấp một cái gì đó
với hiệu suất trong tâm trí, bạn nên sử dụng typeof với phần cứng thông thường, nếu bạn tạo một tập lệnh có vòng lặp 10 triệu lần lặp thì hướng dẫn: typeof str == 'string' sẽ mất 9ms trong khi chuỗi 'chuỗi' của chuỗi sẽ mất 19ms
Tất nhiên là có vấn đề ........!
Chúng ta hãy làm điều này với các ví dụ. Trong ví dụ của chúng tôi, chúng tôi sẽ khai báo hàm theo hai cách khác nhau.
Chúng tôi sẽ sử dụng cả hai function declaration
và Hàm xây dựng . Chúng tôi sẽ làm thế nào typeof
và instanceof
hành xử trong hai kịch bản khác nhau.
Tạo hàm bằng cách khai báo hàm:
function MyFunc(){ }
typeof Myfunc == 'function' // true
MyFunc instanceof Function // false
Có thể giải thích cho kết quả khác nhau như vậy, khi chúng tôi thực hiện khai báo hàm, typeof
có thể hiểu rằng đó là một hàm. typeof
Bởi vì kiểm tra xem biểu thức của loại nào đang hoạt động hay không , trong trường hợp của chúng tôi có thực hiện Phương thức gọi hay không . Nếu nó thực hiện phương thức thì đó là một hàm. Ngược lại, không. Làm rõ kiểm tra đặc tả ecmascript cho typeof .MyFunc
Call
Tạo hàm bằng hàm tạo:
var MyFunc2 = new Function('a','b','return a+b') // A function constructor is used
typeof MyFunc2 == 'function' // true
MyFunc2 instanceof Function // true
Ở đây typeof
khẳng định đó MyFunc2
là một chức năng cũng như instanceof
toán tử. Chúng tôi đã biết typeof
kiểm tra xemMyFunc2
Call
phương thức đã thực hiện hay chưa. Đó MyFunc2
là một hàm và nó thực hiện call
phương thức, đó là cách typeof
biết đó là một hàm. Mặt khác, chúng ta đã sử dụng function constructor
để tạo ra MyFunc2
nó trở thành một ví dụ của Function constructor
. Đó là lý do tại sao instanceof
cũng giải quyết true
.
Những gì an toàn hơn để sử dụng?
Như chúng ta có thể thấy trong cả hai trường hợp, typeof
toán tử có thể khẳng định thành công rằng chúng ta đang xử lý một hàm ở đây, nó an toàn hơn instanceof
. instanceof
sẽ thất bại trong trường hợp function declaration
vì function declarations
không phải là một ví dụ Function constructor
.
Thực hành tốt nhất :
Như Gary Rafferty đã đề xuất, cách tốt nhất nên sử dụng cả typeof và instanceof cùng nhau.
function isFunction(functionItem) {
return typeof(functionItem) == 'function' || functionItem instanceof Function;
}
isFunction(MyFunc) // invoke it by passing our test function as parameter
không cần phải quá tải với hàng tấn ví dụ trên, chỉ cần ghi nhớ hai quan điểm:
typeof var;
là một toán tử đơn nguyên sẽ trả về kiểu gốc hoặc kiểu gốc của var. do đó nó sẽ trở lại loại nguyên thủy ( string
, number
, bigint
, boolean
, undefined
, và symbol
) hoặc object
loại.
trong trường hợp đối tượng cấp cao hơn, như các đối tượng tích hợp (Chuỗi, Số, Boolean, Mảng ..) hoặc các đối tượng phức tạp hoặc tùy chỉnh, tất cả chúng đều là object
loại gốc, nhưng loại đối tượng được xây dựng dựa trên chúng là khác nhau (như lớp OOP Khái niệm thừa kế), ở đây a instanceof A
- một toán tử nhị phân - sẽ giúp bạn, nó sẽ đi qua chuỗi nguyên mẫu để kiểm tra xem hàm tạo của toán hạng bên phải (A) có xuất hiện hay không.
vì vậy bất cứ khi nào bạn muốn kiểm tra "kiểu gốc" hoặc làm việc với biến nguyên thủy - sử dụng "typeof", nếu không thì sử dụng "instanceof".
null
là một trường hợp đặc biệt, có vẻ như nguyên thủy, nhưng thực sự là một trường hợp đặc biệt cho đối tượng. Sử dụng a === null
để kiểm tra null thay thế.
mặt khác, function
cũng là một trường hợp đặc biệt, là đối tượng tích hợp sẵn nhưng typeof
trả vềfunction
như bạn có thể thấy instanceof
phải đi qua chuỗi nguyên mẫu trong khi typeof
chỉ cần kiểm tra loại gốc một lần để dễ hiểu tại sao typeof
nhanh hơninstanceof
Theo tài liệu MDN về typeof , các đối tượng được khởi tạo bằng từ khóa "mới" thuộc loại 'đối tượng':
typeof 'bla' === 'string';
// The following are confusing, dangerous, and wasteful. Avoid them.
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';
Trong khi tài liệu về các điểm thể hiện rằng:
const objectString = new String('String created with constructor');
objectString instanceOf String; // returns true
objectString instanceOf Object; // returns true
Vì vậy, nếu người ta muốn kiểm tra ví dụ rằng một cái gì đó là một chuỗi bất kể nó được tạo ra như thế nào, cách tiếp cận an toàn nhất sẽ được sử dụng instanceof
.