JS高階之面試必須知道的幾個點

小小小發表於2018-06-08

深圳市人人聚財招前端啦 ~~~

招前端,招前端,招前端,歡迎掃碼加群,砸簡歷過來:

JS高階之面試必須知道的幾個點

前言

這段時間突然發現JS原生好多東西都忘記了,但有些東西確實很重要,所以又重新再梳理一次。主要有函式的3種定義方法,ES5函式this指向,call與appl用法,JS常見的4種設計模式,原型鏈,原型鏈和繼承的方式(ES5和ES6)

1.函式的3種定義方法

1.1 函式宣告

//ES5
function getSum(){}
function (){}//匿名函式
//ES6
()=>{}//如果{}內容只有一行{}和return關鍵字可省,
複製程式碼

1.2 函式表示式(函式字面量)

//ES5
var sum=function(){}
//ES6
let sum=()=>{}//如果{}內容只有一行{}和return關鍵字可省,
複製程式碼

1.3 建構函式

var sum=new GetSum(num1,num2)
複製程式碼

1.4 三種方法的對比

1.函式宣告有預解析,而且函式宣告的優先順序高於變數;
2.使用Function建構函式定義函式的方式是一個函式表示式,這種方式會導致解析兩次程式碼,影響效能。第一次解析常規的JavaScript程式碼,第二次解析傳入建構函式的字串

2.ES5中函式的4種呼叫

在ES5中函式內容的this指向和呼叫方法有關

2.1 函式呼叫模式

包括函式名()和匿名函式呼叫,this指向window

 function getSum() {
    console.log(this) //window
 }
 getSum()
 
 (function() {
    console.log(this) //window
 })()
 
 var getSum=function() {
    console.log(this) //window
 }
 getSum()
複製程式碼

2.2 方法呼叫

物件.方法名(),this指向物件

var objList = {
   name: 'methods',
   getSum: function() {
     console.log(this) //objList物件
   }
}
objList.getSum()
複製程式碼

2.3 構造器呼叫

new 建構函式名(),this指向建構函式

function Person() {
  console.log(this); //指向建構函式Person
}
var personOne = new Person();
複製程式碼

2.4 間接呼叫

利用call和apply來實現,this就是call和apply對應的第一個引數,如果不傳值或者第一個值為null,undefined時this指向window

function foo() {
   console.log(this);
}
foo.apply('我是apply改變的this值');//我是apply改變的this值
foo.call('我是call改變的this值');//我是call改變的this值
複製程式碼

3.ES6中函式的呼叫

箭頭函式不可以當作建構函式使用,也就是不能用new命令例項化一個物件,否則會丟擲一個錯誤
箭頭函式的this是和定義時有關和呼叫無關
呼叫就是函式呼叫模式

(() => {
   console.log(this)//window
})()

let arrowFun = () => {
  console.log(this)//window
}
arrowFun()

let arrowObj = {
  arrFun: function() {
   (() => {
     console.log(this)//arrowObj
   })()
   }
 }
 arrowObj.arrFun();
複製程式碼

4.call,apply和bind

1.IE5之前不支援call和apply,bind是ES5出來的;
2.call和apply可以呼叫函式,改變this,實現繼承和借用別的物件的方法;

4.1 call和apply定義

呼叫方法,用一個物件替換掉另一個物件(this)
物件.call(新this物件,實參1,實參2,實參3.....)
物件.apply(新this物件,[實參1,實參2,實參3.....])

4.2 call和apply用法

1.間接呼叫函式,改變作用域的this值
2.劫持其他物件的方法

var foo = {
  name:"張三",
  logName:function(){
    console.log(this.name);
  }
}
var bar={
  name:"李四"
};
foo.logName.call(bar);//李四
實質是call改變了foo的this指向為bar,並呼叫該函式
複製程式碼

3.兩個函式實現繼承

function Animal(name){   
  this.name = name;   
  this.showName = function(){   
    console.log(this.name);   
  }   
}   
function Cat(name){  
  Animal.call(this, name);  
}    
var cat = new Cat("Black Cat");   
cat.showName(); //Black Cat
複製程式碼

4.為類陣列(arguments和nodeList)新增陣列方法push,pop

(function(){
  Array.prototype.push.call(arguments,'王五');
  console.log(arguments);//['張三','李四','王五']
})('張三','李四')
複製程式碼

5.合併陣列

let arr1=[1,2,3]; 
let arr2=[4,5,6]; 
Array.prototype.push.apply(arr1,arr2); //將arr2合併到了arr1中
複製程式碼

6.求陣列最大值

Math.max.apply(null,arr)
複製程式碼

7.判斷字元型別

Object.prototype.toString.call({})
複製程式碼

4.3 bind

bind是function的一個函式擴充套件方法,bind以後程式碼重新繫結了func內部的this指向,不會呼叫方法,不相容IE8

var name = '李四'
 var foo = {
   name: "張三",
   logName: function(age) {
   console.log(this.name, age);
   }
 }
 var fooNew = foo.logName;
 var fooNewBind = foo.logName.bind(foo);
 fooNew(10)//李四,10
 fooNewBind(11)//張三,11  因為bind改變了fooNewBind裡面的this指向
複製程式碼

5.JS常見的四種設計模式

5.1 工廠模式

簡單的工廠模式可以理解為解決多個相似的問題;

function CreatePerson(name,age,sex) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sex = sex;
    obj.sayName = function(){
        return this.name;
    }
    return obj;
}
var p1 = new CreatePerson("longen",'28','男');
var p2 = new CreatePerson("tugenhua",'27','女');
console.log(p1.name); // longen
console.log(p1.age);  // 28
console.log(p1.sex);  // 男
console.log(p1.sayName()); // longen

console.log(p2.name);  // tugenhua
console.log(p2.age);   // 27
console.log(p2.sex);   // 女
console.log(p2.sayName()); // tugenhua  
複製程式碼

5.2單例模式

只能被例項化(建構函式給例項新增屬性與方法)一次

// 單體模式
var Singleton = function(name){
    this.name = name;
};
Singleton.prototype.getName = function(){
    return this.name;
}
// 獲取例項物件
var getInstance = (function() {
    var instance = null;
    return function(name) {
        if(!instance) {//相當於一個一次性閥門,只能例項化一次
            instance = new Singleton(name);
        }
        return instance;
    }
})();
// 測試單體模式的例項,所以a===b
var a = getInstance("aa");
var b = getInstance("bb");  
複製程式碼

5.3 沙箱模式

將一些函式放到自執行函式裡面,但要用閉包暴露介面,用變數接收暴露的介面,再呼叫裡面的值,否則無法使用裡面的值

let sandboxModel=(function(){
    function sayName(){};
    function sayAge(){};
    return{
        sayName:sayName,
        sayAge:sayAge
    }
})()
複製程式碼

5.4 釋出者訂閱模式

就例如如我們關注了某一個公眾號,然後他對應的有新的訊息就會給你推送,

//釋出者與訂閱模式
    var shoeObj = {}; // 定義釋出者
    shoeObj.list = []; // 快取列表 存放訂閱者回撥函式

    // 增加訂閱者
    shoeObj.listen = function(fn) {
        shoeObj.list.push(fn); // 訂閱訊息新增到快取列表
    }

    // 釋出訊息
    shoeObj.trigger = function() {
            for (var i = 0, fn; fn = this.list[i++];) {
                fn.apply(this, arguments);//第一個引數只是改變fn的this,
            }
        }
     // 小紅訂閱如下訊息
    shoeObj.listen(function(color, size) {
        console.log("顏色是:" + color);
        console.log("尺碼是:" + size);
    });

    // 小花訂閱如下訊息
    shoeObj.listen(function(color, size) {
        console.log("再次列印顏色是:" + color);
        console.log("再次列印尺碼是:" + size);
    });
    shoeObj.trigger("紅色", 40);
    shoeObj.trigger("黑色", 42);  
複製程式碼

程式碼實現邏輯是用陣列存貯訂閱者, 釋出者回撥函式裡面通知的方式是遍歷訂閱者陣列,並將釋出者內容傳入訂閱者陣列

更多設計模式請戳:Javascript常用的設計模式詳解

6.原型鏈

6.1 定義

物件繼承屬性的一個鏈條

6.2建構函式,例項與原型物件的關係

圖片描述
圖片描述

var Person = function (name) { this.name = name; }//person是建構函式
var o3personTwo = new Person('personTwo')//personTwo是例項
複製程式碼

圖片描述
圖片描述

原型物件都有一個預設的constructor屬性指向建構函式

6.3 建立例項的方法

1.字面量

let obj={'name':'張三'}
複製程式碼

2.Object建構函式建立

let Obj=new Object()
Obj.name='張三'
複製程式碼

3.使用工廠模式建立物件

function createPerson(name){
 var o = new Object();
 o.name = name;
 };
 return o; 
}
var person1 = createPerson('張三');
複製程式碼

4.使用建構函式建立物件

function Person(name){
 this.name = name;
}
var person1 = new Person('張三');
複製程式碼

6.4 new運算子

1.創了一個新物件;
2.this指向建構函式;
3.建構函式有返回,會替換new出來的物件,如果沒有就是new出來的物件
4.手動封裝一個new運算子

var new2 = function (func) {
    var o = Object.create(func.prototype);    //建立物件
    var k = func.call(o);             //改變this指向,把結果付給k
    if (typeof k === 'object') {         //判斷k的型別是不是物件
        return k;                  //是,返回k
    } else {
        return o;                  //不是返回返回建構函式的執行結果
    }
}  
複製程式碼

更多詳情:詳談JavaScript原型鏈

6.5 物件的原型鏈

圖片描述
圖片描述

7.繼承的方式

JS是一門弱型別動態語言,封裝和繼承是他的兩大特性

7.1原型鏈繼承

將父類的例項作為子類的原型
1.程式碼實現
定義父類:

// 定義一個動物類
function Animal (name) {
  // 屬性
  this.name = name || 'Animal';
  // 例項方法
  this.sleep = function(){
    console.log(this.name + '正在睡覺!');
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
  console.log(this.name + '正在吃:' + food);
};
複製程式碼

子類:

function Cat(){ 
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();
console.log(cat.name);//cat
console.log(cat.eat('fish'));//cat正在吃:fish  undefined
console.log(cat.sleep());//cat正在睡覺! undefined
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true
複製程式碼

2.優缺點
簡單易於實現,但是要想為子類新增屬性和方法,必須要在new Animal()這樣的語句之後執行,無法實現多繼承

7.2 構造繼承

實質是利用call來改變Cat中的this指向
1.程式碼實現
子類:

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
複製程式碼

2.優缺點
可以實現多繼承,不能繼承原型屬性/方法

7.3 例項繼承

為父類例項新增新特性,作為子類例項返回
1.程式碼實現
子類

function Cat(name){
  var instance = new Animal();
  instance.name = name || 'Tom';
  return instance;
}
複製程式碼

2.優缺點
不限制呼叫方式,但不能實現多繼承

7.4 拷貝繼承

將父類的屬性和方法拷貝一份到子類中
1.子類:

function Cat(name){
  var animal = new Animal();
  for(var p in animal){
    Cat.prototype[p] = animal[p];
  }
  Cat.prototype.name = name || 'Tom';
}
複製程式碼

2.優缺點
支援多繼承,但是效率低佔用記憶體

7.5 組合繼承

通過呼叫父類構造,繼承父類的屬性並保留傳參的優點,然後通過將父類例項作為子類原型,實現函式複用
1.子類:

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
複製程式碼

7.6 寄生組合繼承

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
(function(){
  // 建立一個沒有例項方法的類
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //將例項作為子類的原型
  Cat.prototype = new Super();
})();
複製程式碼

7.7 ES6的extends繼承

ES6 的繼承機制是先創造父類的例項物件this(所以必須先呼叫super方法),然後再用子類的建構函式修改this,連結描述

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 呼叫父類的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 呼叫父類的toString()
  }
}   
複製程式碼

更多詳情請戳:JS繼承的實現方式

參考文獻:

www.cnblogs.com/tugen...
www.cnblogs.com/humin...
www.cnblogs.com/cheng...
www.cnblogs.com/cheng...

相關文章