首頁»JavaScript»JS高級之面試必須知道的幾個點

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

                    來源:火狼 發布時間:2018-04-18 閱讀次數:

                    前言

                    這段時間突然發現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 getSum(){}
                    //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繼承的實現方式

                    QQ群:WEB開發者官方群(515171538),驗證消息:10000
                    微信群:加小編微信 849023636 邀請您加入,驗證消息:10000
                    提示:更多精彩內容關注微信公眾號:全棧開發者中心(fsder-com)
                    網友評論(共0條評論) 正在載入評論......
                    理智評論文明上網,拒絕惡意謾罵 發表評論 / 共0條評論
                    登錄會員中心
                    福彩试机号今天