乐博娱乐»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继续的实现方式