• JS高级之面试必须知道的几个点


    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...

  • 相关阅读:
    LFI、RFI、PHP封装协议安全问题学习
    Django:(博客系统)添加文章(中文)出现UnicodeEncodeError乱码
    Django:(博客系统)使用使用mysql数据->后台管理tag/post/category的配置
    Spark:性能调优
    Django:(博客系统)使用使用mysql数据&创建post/category/tag实体,并同步到数据中
    C#:多进程开发,控制进程数量
    Django通过pycharm创建后,如何登录admin后台?
    扑克牌游戏研究先告一段落
    Zookeeper通过java创建、查看、修改、删除znode
    ZooKeeper:win7上安装单机及伪分布式安装
  • 原文地址:https://www.cnblogs.com/kkdn/p/9167599.html
Copyright © 2020-2023  润新知