浅论typeof和instanceof的区别以及instanceof的实现原理+NaN详解

typeof的返回值是一个字符串,用来说明变量的数据类型;instanceof的返回值是布尔值,用于判断一个变量是否属于某个对象的实例。

typeof:

const type =  typeof '中国万岁'; // string
typeof 666; // number
typeof true; // boolean
typeof undefined; // undefined
typeof Symbol(); // symbol
typeof 1n; // bigint
typeof () => {}; // function

typeof []; // object
typeof {}; // object
typeof new String('xxx'); // object
typeof null; // object

   let a=new Number("a") //Number是js的内置对象
    console.log(typeof a); //object
    console.log(a); //Number构造函数,但是属性对应的值是NaN

    let b=new Number(2) //或者new Number("2") 
    console.log(typeof b); //object
    console.log(b); //Number构造函数,但是属性对应的值是2

通过以上例子可以看出,typeof只能准确判断原始数据类型和函数(函数其实是对象,并不属于另一种数据类型,但也能够使用 typeof 进行区分),无法精确判断出引用数据类型(统统返回 object)。
有一点需要注意,调用typeof null返回的是object,这是因为特殊值null被认为是一个空对象的引用(也叫空对象指针)。
如果想准确判断引用数据类型,可以用instanceof运算符。

instanceof:
instanceof运算符返回一个布尔值,可以用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,通俗说就是用于判断某个实例是否属于某构造函数。再通俗一点就是,只要右边变量的prototype在左边变量的原型链上就返回 true,否则返回 false

const result = [];
result instanceof Array   // true
const result = {};
console.log(result instanceof Object); //true  instanceof 可以准确判断引用数据类型
const result = null;
console.log(result instanceof Object); //flase  instanceof不能正确判断原始数据类型
console.log(result instanceof Null);  //Null is not defined

const Person = function() {};
const p = new Person();
p instanceof Person; // true
console.log(p instanceof Object); //true

const message = new String('xxx'); //这个String构造函数是原生js自己带的
message instanceof String; // true
console.log(message instanceof Object); //true

let str = 'xxx'
str instanceof String // false

区别:
1.typeof 会返回一个运算数的基本类型,instanceof 返回的是布尔值
2.instanceof 可以准确判断引用数据类型,但是不能正确判断原始数据类型
3.typeof 虽然可以判断原始数据类型(null 除外),但是无法判断引用数据类型(function 除外)
4.原始数据类型null要想准确判断只有用 Object.prototype.toString.call(null) //"[object Null]"

关于instanceof的实现原理:

function myInstanceof(left, right) {
    // 这里先用typeof来判断基础数据类型,如果是,直接返回false
    if(typeof left !== 'object' || left === null) return false;
    // getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
    let proto = Object.getPrototypeOf(left);  // 等同于let proto=left.__proto__;

    while(true) {                  
        if(proto === null) return false;
        if(proto === right.prototype) return true;//找到相同原型对象,返回true
        proto = Object.getPrototypeof(proto);  //  proto=proto.__proto__;
    }
}

也就是顺着原型链去找,直到找到相同的原型对象,返回true,否则为false

typeof和instanceof都有一定的弊端,并不能满足所有场景的需求。如果需要通用检测数据类型,可以使用Object.prototype.toString.call()方法:

Object.prototype.toString({})       // 所有的数据类型不加call都是"[object Object]"
Object.prototype.toString.call({})  // "[object Object]"
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]"

const Person = function() {};
console.log(Object.prototype.toString.call(new Person())); //[object Object]

Object.prototype.toString.call([])       //"[object Array]"
Object.prototype.toString.call(document)  //"[object HTMLDocument]"
Object.prototype.toString.call(window)   //"[object Window]"

注意,该方法返回的是一个格式为"[object Object]"的字符串。

封装函数:
为了更方便的使用,我们可以将这个方法进行封装:

function getType(obj){
  let type  = typeof obj;
  if (type !== "object") {    // 先进行typeof判断,如果是基础数据类型和函数,直接返回
    return type;
  }
  // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1'); 
}

getType([])     // "Array" typeof []是object,因此toString返回
getType('123')  // "string" typeof 直接返回
getType(window) // "Window" toString返回
getType(null)   // "Null"首字母大写,typeof null是object,需toString来判断
getType(undefined)   // "undefined" typeof 直接返回
getType()            // "undefined" typeof 直接返回
getType(function(){}) // "function" typeof能判断,因此首字母小写
getType(/123/g)      //"RegExp" toString返回

js的字符串在进行大于(小于)比较时,会根据第一个不同的字符的ASCII码值进行比较。如果第一个字符的ASCII码值相同,则比较第二个;依次类推

console.log('2'>'11') //true  //2的ASCII码值大  //所以同是字符型数字比较不会把字符整数转化成number型
console.log(2>'11') //false  //一个字符型和一个number型运算时,除了加运算,其他的乘除取模减以及比较都会把字符整数转化成number型再去运算
 console.log('11'/2)  //5.5   typeof number
onsole.log(2+'11') //211      typeof string
 console.log('2'+11) //211
console.log('15'>'3') //flase  //3的ASCII码值大
console.log('15'>3) //true  //一个字符型和一个number型运算时,除了加运算,其他的乘除取模减以及比较都会把字符整数转化成number型再去运算
console.log('a'>'b') //false
console.log('abc'>'a') //true
console.log('abc'>'acb') //false

JS的类型检测方法:
1.typeof ,但只能准确判断基本数据类型(null除外),无法精确判断出引用数据类型(function 除外)
2. instanceof通过判断原型,但只可以准确判断引用数据类型,但是不能正确判断原始数据类型,返回 true或 false
3.Object.prototype.toString.call()方法可以作为通用检测数据类型

JS中的NaN:
JavaScript中,NaN是一个特殊的数字值(typeof NaN的结果为number),是not a number的缩写,表示不是一个合法的数字。

1.NaN的产生:

Number('abc') // NaN
Number(undefined) // NaN

2.注意点:NaN是唯一一个和自身不相等的值:

//NaN 永远不会等于NaN,因为NaN虽然表示的不是数字, 但是具体值也是不同的,因此 NaN 不等于 NaN。
NaN === NaN  // false 
NaN == NaN // false

3.如何辨别NaN:
我们可以使用全局函数isNaN()来判断一个数值是不是一个非数字(并不是用来判断是不是NaN这个值):

isNaN(NaN)  // true
isNaN(10)  // false

4.NaN详解点此

5.附

let v=2
let r=8
console.log(v=r); //8 相当于执行完赋值返回v的值
console.log(v); //8 

你可能感兴趣的:(前端面试题,javascript,原型模式,开发语言)