前端面试 | 八股文

693 阅读5分钟

logo_siyu

1. 判断一个变量是数组还是对象

调用原型链的toString方法

        var a = []
        var obj = {}
        //方法1
        console.log(Array.isArray(a));

	//方法2
        console.log(a instanceof Array);

	//方法3
        console.log(a.constructor === Array);
	//instanceof 和 constructor 在企业级开发中使用很少,这两个的弊端是什么?
	//iframe是个特例,这两个方法不能判断

	//方法4
        // Object.prototype.toString.call 通过call()方法调用原型链顶端的toString方法
        console.log(Object.prototype.toString.call(a) === '[object Array]');
        //其实在这里直接调用obj.toString也可以得到[object Object],为什么还要调用原型链顶端的toString方法?
        // 因为对象中的toString方法可以被认为改变
        console.log(Object.prototype.toString.call(obj) === '[object Object]');

2. JS如何实现继承?

// 方法1:借助call (call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数)
 function Parent1(){
    this.name = 'parent1';
    this.play = function(){
        console.log('zhge')
    }
 }

 function Child1(){
    Parent1.call(this);
    this.type = 'child1'
 }

 console.log(new Child1)
// 方法2:借助原型链
function Parent2(){
    this.name = 'parent2';
    this.play = [1,2,3];
}

function Child2(){
    this.type = 'child2';
}

Child2.prototype = new Parent2()

console.log(new Child2())

var s1 = new Child2()
var s2 = new Child2()
s1.play.push(4)

console.log(s1.play, s2.play)//[1,2,3,4]    [1,2,3,4]
//将前两种方法组合
function Parent3(){
    this.name = 'parent3';
    this.play = [1,2,3];
}

function Child3(){
    Parent3.call(this)
    this.type = 'child3'
}

Child3.prototype = new Parent3()

var s3 = new Child3()
var s4 = new Child3()
s3.play.push(4)
console.log(s3.play, s4.play) //[1,2,3,4]    [1,2,3]
// 方法4:组合继承的优化
function Parent4(){
    this.name = 'parent4'
    this.play = [1, 2, 3]
}

function Child4(){
    Parent4.call(this);
    this.type = 'Child4';
}

Child4.prototype = Parent4.prototype

var s3 = new Child4()
var s4 = new Child4()
s3.play.push(4)
console.log(s3)
//Child4 {name: 'parent4', play: Array(3), type: 'Child4'}
//name: "parent4"
//play: (3) [1, 2, 3]
//type: "Child4"
//[[Prototype]]: Object

console.log(s3.play, s4.play)//[1,2,3,4]    [1,2,3]
// 方法5(最推荐)【寄生组合继承寄生组合继承】:组合继承的优化
function Parent5(){
    this.name = 'parent5';
    this.play = [1, 2, 3]
}

function Child5(){
    Parent5.call(this);
    this.type = 'child5';
}

Child5.prototype = Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;

var s3 = new Child5()
var s4 = new Child5()
console.log(s3)

// Child5 {name: 'parent5', play: Array(3), type: 'child5'}
// name: "parent5"
// play: (4) [1, 2, 3, 4]
// type: "child5"
// [[Prototype]]: Parent5

3. typeof运算符和instanceof运算符以及isPrototypeOf()方法的区别

  • typeof是一个运算符,用于检测数据的类型,比如基本数据类型null、undefined、string、number、boolean,以及引用数据类型object、function,但是对于正则表达式、日期、数组这些引用数据类型,它会全部识别为object;
  • instanceof同样也是一个运算符,用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,只返回true或者false值;用法:某个实例对象   instanceof   某个构造函数.
  • isPrototypeOf是用来检测调用此方法的对象是否存在于另一个对象的原型链中,所以本质上就是检测目标不同。

4. call()和apply()的区别

  • call():第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。在使用call()方法时,传递给函数的参数必须逐个列举出来;
  • apply():传递给函数的是参数数组。

5. 全局函数eval()有什么作用?

  • eval() 只有一个参数,如果传入的参数不是字符串,它直接返回这个参数。
  • 如果参数是字符串,它会把字符串当成javascript代码进行编译。
    • 如果编译失败则抛出一个语法错误(syntaxError)异常;
    • 如果编译成功,则开始执行这段代码,并
      • 返回字符串中的最后一个表达式或语句的值;
      • 如果最后一个表达式或语句没有值,则最终返回undefined;
      • 如果字符串抛出一个异常,这个异常将把该调用传递给eval()。

6. 描述以下变量的区别:null,undefined或undeclared

null 表示"没有对象",即该处不应该有值,转为数值时为0。典型用法是:

  1. 手动释放内存
  2. 作为函数的参数,表示该函数的参数不是对象。
  3. 作为对象原型链的终点。

undefined 表示"缺少值",就是此处应该有一个值,但是还没有定义,转为数值时为NaN。典型用法是:

  1. 变量被声明了,但没有赋值时,就等于undefined。
  2. 调用函数时,应该提供的参数没有提供,该参数等于undefined。
  3. 对象没有赋值的属性,该属性的值为undefined。
  4. 函数没有返回值时,默认返回undefined。

undeclared :js语法错误,没有声明直接使用,js无法找到对应的上下文

7. ==和===有什么区别?

  • == equality 等同,两边值类型不同的时候,要先进行类型转换,再比较;
  • === identity 恒等,不做类型转换,类型不同的一定不等。

8. 同步异步?

1、进程同步:就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事;

2、异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

9. 什么是事件代理/事件委托?

事件代理/事件委托是利用事件冒泡的特性,将本应该绑定在多个元素上的事件绑定在他们的祖先元素上,尤其在动态添加子元素的时候,可以非常方便的提高程序性能,减小内存空间。

10. 什么是事件冒泡?什么是事件捕获?

冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。

捕获型事件:事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定)。

11. 简述javascript中this的指向

第一准则是:this永远指向函数运行时所在的对象,而不是函数被创建时所在的对象。

  • 普通的函数调用,函数被谁调用,this就是谁。
  • 构造函数的话,如果不用new操作符而直接调用,那即this指向window。用new操作符生成对象实例后,this就指向了新生成的对象。
  • 匿名函数或不处于任何对象中的函数指向window 。
  • 如果是call,apply等,指定的this是谁,就是谁。