【CSDN學院出品】 你不可不知的JS面試題(分期更新……)

Vam的金豆之路發表於2020-02-15

1、JS中有哪些內建型別?

7種。分別是booleannumberstringobjectundefinednullsymbol

2、NaN是獨立的一種型別嗎?

不是。NaN是number型別。

3、如何判斷是哪個型別?

Object.prototype.toString.call(),返回為[object Type]

現在我們來驗證一下。

Object.prototype.toString.call(NaN);
// "[object Number]"
Object.prototype.toString.call('1');
// "[object String]"
Object.prototype.toString.call([1,2]);
// "[object Array]"

為什麼使用Object.prototype.toString.call(),而不用typeof。是因為它有一點具有侷限性。比如當我們判斷陣列型別時,列印出的是object,而不是我們想要的Array

typeof [1,2];
// "object"
typeof new Number(1);
// "object"

好,既然Object.prototype.toString.call()這麼好用,我們不如封裝一個方法。

function getType(v) {
	return Object.prototype.toString.call(v).match(/\[object (.+?)\]/)[1].toLowerCase();
}
getType(1);
// "number"

4、內建型別之間如何進行型別轉換?

首先,我們先看幾個例子。

第一個例子:

const a = 1;
const b = '1';
if(a==b){
	console.log('true') //true
}

第二個例子:

const x = 1+'1';
console.log(x); // 11
const y = 1*'1'; 
console.log(y); // 1

第三個例子:

const array = [];
if (array) {
	console.log('true'); // 返回true
} else {
	console.log('false');
}

第四個例子:

if([] == false) {
console.log('true'); //返回true
} else {
console.log('false');
}

如果你還不是不相信第四個例子的操作,那麼我們實際操作一下。果不其然,還是返回true
在這裡插入圖片描述
我們先不著急,想它為什麼會返回true。我們需要知道這兩點。

四則運算轉化: 

 1. 當遇到和字串做 “加法” 時,會先將其轉化為字串,然後再進行字串相加。
 2. 當遇到“減法”、“乘法”、“除法”時,則會將其轉化為數字,然後再進行運算。

對比轉化(==):

 - 布林值、字串轉化為數字 ;
 - 物件轉化為字串(絕大多數);
 - null、undefined、symbol、NaN;

我們先看下對比轉化中的第三點。

  • null == undefined ,但是不等於其他。
    在這裡插入圖片描述
    在這裡插入圖片描述
  • symbol 不等於其他。
    在這裡插入圖片描述
  • NaN 不等於其他,關鍵是和自己都不相等。
    在這裡插入圖片描述
    好了,轉為正題。第四個例子為什麼會返回ture。因為[]先轉化為字串(對比轉化中說過)。使用toString()方法轉化,也就是'',然後''轉化為數字0,最後false轉化為000當然相等。

說了這麼多,我們仔細來講講物件的資料型別轉換。

物件的資料型別轉換
  • valueOf
  • toString
  • Symbol.toPrimitive

我們先來個例子:

   let obj = {
        valueOf(){
            return 1
        },
        toString(){
            return '字元'
        }
    };
    console.log(obj+1);

你是不是會覺得會是[object Object]1。告訴你,不是哦!而是2。因為valueOftoString是物件內建的方法。這裡我們只是自定義了一下。我們先來證明一下是不是真的是內建的方法。
在這裡插入圖片描述
valueOf方法是轉換原始值,也就是自身。toString方法是轉換為字元。
對了,為什麼是2呢?這就是物件的資料轉換的靈活性,它會根據自身環境的適應性。轉換自身,這裡我們使用obj+1,會優先使用數字相加。那麼我們換成alert(obj)

    let obj = {
        valueOf(){
            return 1
        },
        toString(){
            return '字元'
        }
    };
    alert(obj); 

這裡則會列印出字元。因為alert方法會優先使用字元型別。

最後,我們來講下最後的一個屬性Symbol.toPrimitive

    let obj = {
        valueOf(){
            return 1
        },
        toString(){
            return '字元'
        },
        [Symbol.toPrimitive](){
            return 10
        }
    }
    console.log(obj+1) // 11
    alert(obj); //10 

不論是相加操作,還是alert彈出。都是執行Symbol.toPrimitive。因為它的權重最高。

5、this是什麼?

this指代表當前環境的上下文。

6、如何判斷this的指向

  1. 預設情況(誰的方法就指向誰)
  2. 顯示繫結
  3. 箭頭函式
  4. 嚴格模式
第一種情況(誰的方法就指向誰)

1、

    var a = 2;
    var obj = {
      a:1,
      getVal(){
          console.log(this.a); 
      }
    }
    obj.getVal(); //1

2、

   class A {
       a(){
        console.log(this);
       }
   };
   const f = new A();
   f.a(); // A {}

3、

  function fun() {
      console.log(this)
  };
  fun(); //window,這裡相當於window.fun()。
第二種情況(顯示繫結)

callapplybind方法都能顯示改變this的指向。

 var value = 3;
    var obj = {
        value: 1
    }
    function get() {
        console.log(this.value); 
    }
    get(); // 3
    get.call(obj); // 1
    get.apply(obj); // 1
    get.bind(obj)(); // 1
第三種情況(箭頭函式)

箭頭函式的thisfunction中的this指向不同,它指向其外部的上下文。

    var value = 3;
    var obj = {
        value: 1,
        get: ()=> {
            console.log(this.value);
        }
    }
    obj.get(); // 3
第四種情況 (嚴格模式

嚴格模式下,方法直接被呼叫的時候,方法內部的this不會指向window

'use strict'
function fun() {
    console.log(this)
}
fun(); //undefined

7、什麼是prototype?

prototype就是原型物件,它是函式所獨有的,它包含了一個函式(類)所有的例項共享的屬性和方法。

function A() {};
A.prototype.get=()=>{
    console.log('我是get方法');
}
var a1 = new A();
a1.get(); // 我是get方法
var a2= new A();
a2.get(); // 我是get方法

a1a2都是A的例項,所以他們都有A的原型物件上的get方法屬性。

8、將方法設定在prototype上和設定在建構函式的this上有什麼區別?

 function A() {};
    A.prototype.get = () => {
        console.log('我是A');
    }

 function B() {
        this.get = () => {
            console.log('我是B');
        }
    };
  • 繫結在prototype上的方法只會在記憶體中儲存一份,每個例項都會根據原型鏈找到建構函式上的這個方法,然後呼叫。

  • 繫結在建構函式this上的方法會在每次例項化的時候都在記憶體中建立一次,也是new幾次,就會建立幾次。

9、什麼是__proto__?

  • _proto_是瀏覽器內部的屬性,並非js標準屬性。(一般我們不會直接操作它)
  • 每個物件都有_proto_,且指向建構函式的prototype。(非常重要,但是nullundefined沒有_proto_

第二點注意,下面兩個例子都返回true

 function F() {};
 const f = new F();
 f.__proto__ === F.prototype?console.log('true'):console.log('false'); //true
    
const n = new Number(1);
n.__proto__ === Number.prototype?console.log('true'):console.log('false'); // true

10、物件的原型鏈組成?

原型鏈:在這裡插入圖片描述

   function A() {};
   const a = new A();
   a.__proto__===A.prototype?console.log('true'):console.log('false');//true
   console.log(A.prototype); // {constructor:f}
   A.prototype.get=()=>{
       console.log('這是get')
   }
   console.log(A.prototype); // {get:f,constructor:f} //列印出為一個物件
   // 以下兩行程式碼為舉例
   const obj = new Object({get:()=>{},constructor:()=>{}})
   console.log(obj);
   
   A.prototype.__proto__===Object.prototype?console.log('true'):console.log('false');//true

11、建構函式的原型鏈?

眾所周知,建構函式也是物件。我們知道建立建構函式有兩種方法。
1、

   function A() {};
   console.log(A); //ƒ A() {}

2、

   const B = new Function();
   console.log(B); //ƒ anonymous() {}

所以說,我們看下A.__proto===Function.prototype是否成立。

   function A() {};
   A.__proto__===Function.prototype?console.log('true'):console.log('false');//true

同理,建構函式的原型鏈:
在這裡插入圖片描述
驗證一下,

Function.prototype.__proto__===Object.prototype?console.log('true'):console.log('false');//true

那麼,我們又有另一個疑問?Function.__proto__指向誰呢?

Function.__proto__===Function.prototype?console.log('true'):console.log('false');//true

Function.prototype比較特殊,它是瀏覽器自身建立出來的內建物件。同樣,Object.prototype也是瀏覽器引擎建立出來的內建物件,它們都指向null
在這裡插入圖片描述
在這裡插入圖片描述

12、prototype上的constructor是什麼?

每一個函式的原型物件上的constructor都指向函式本身,目前它並沒有什麼作用,或許可以當作instanceof來使用(當然constructor的指向也是可以被改變的,不過真的沒啥用)

 function A() {};
 A.prototype.constructor===A?console.log('true'):console.log('false');//true
function A() {};
const a = new A();
console.log(a instanceof A) // true

13、call、apply、bind的作用是什麼?

它們都是為了改變方法內部this的指向。

14、call、apply怎麼區別?

  • callapply第一個引數均為this的指向。
  • call的其餘引數就是一個普通的引數列表。
  • apply除了第一個引數外,只接受一個陣列型別的引數。

call的用法:

    const obj = {name:'maomin'};
    function get(age,sex) {
        console.log(`
        我的名字:${this.name}
        我的年齡:${age}
        我的性別:${sex}
        `)
    }
    get.call(obj,18,'男');
    //我的名字:maomin
    //我的年齡:18
    //我的性別:男

apply的用法:

 const obj = {name:'maomin'};
    function get(age,sex) {
        console.log(`
        我的名字:${this.name}
        我的年齡:${age}
        我的性別:${sex}
        `)
    }
    get.apply(obj,[18,'男']);
    //我的名字:maomin
    //我的年齡:18
    //我的性別:男

15、bind與call和apply的不同?

  • 第一個引數為this的指向,其餘引數是一個普通的引數列表。(這一點跟call很像)
  • 返回的是一個函式,需要再呼叫一下。 (bind可以實現柯里化)

1、

    function get(x,y,z) {
        console.log(x,y,z)
    }
    console.log(get.bind(null,'紅','黃','藍'))
    // ƒ (x,y,z) {
    // console.log(x,y,z)
    // }
    get.bind(null,'紅','黃','藍')(); // 紅 黃 藍

2、

    function get(x,y,z) {
        console.log(this.name,x,y,z)
    }
    get.bind({name:'maomin'},'紅','黃','藍')(); // maomin 紅 黃 藍

柯里化:

  function get(x,y,z) {
        console.log(this.name,x,y,z)
    }
   get.bind({name:'maomin'}).bind(null,'紅').bind(null,'黃').bind(null,'藍')(); // maomin 紅 黃 藍

下一期更新 請關注 第二期

相關文章