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。典型用法是:
- 手动释放内存
- 作为函数的参数,表示该函数的参数不是对象。
- 作为对象原型链的终点。
undefined 表示"缺少值",就是此处应该有一个值,但是还没有定义,转为数值时为NaN。典型用法是:
- 变量被声明了,但没有赋值时,就等于undefined。
- 调用函数时,应该提供的参数没有提供,该参数等于undefined。
- 对象没有赋值的属性,该属性的值为undefined。
- 函数没有返回值时,默认返回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是谁,就是谁。