js面试题
JS的基本数据类型和引用数据类型
基本数据类型:undefined、null、boolean、number、string、symbol
引用数据类型:object、array、function
介绍JS有哪些内置对象?
数据封装类对象:Object、Array、Boolean、Number、String
其他对象:Function、Arguments、Math、Date、RegExp、Error
ES6新增对象:Symbol、Map、Set、Promises、Proxy、Reflect
JavaScript有几种类型的值?,你能画一下他们的内存图吗
原始数据类型(Undefined,Null,Boolean,Number、String)-- 栈
引用数据类型(对象、数组和函数)-- 堆
两种类型的区别是:存储位置不同:
原始数据类型是直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据;
引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定,如果存储在栈中,将会影响程序运行的性能;
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。
当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
如何编写高性能的JavaScript?
遵循严格模式:"use strict"; 将js脚本放在页面底部,加快渲染页面;将js脚本将脚本成组打包,减少请求;使用非阻塞方式下载js脚本;尽量使用局部变量来保存全局变量;尽量减少使用闭包;使用 window 对象属性方法时,省略 window;尽量减少对象成员嵌套;缓存 DOM 节点的访问
通过避免使用 eval() 和 Function() 构造器;给 setTimeout() 和 setInterval() 传递函数而不是字符串作为参数;尽量使用直接量创建对象和数组;最小化重绘(repaint)和回流(reflow)
解释JavaScript中的作用域与变量声明提升?
JavaScript作用域:
在Java、C等语言中,作用域为for语句、if语句或{}内的一块区域,称为作用域;
而在 JavaScript 中,作用域为function(){}内的区域,称为函数作用域。
JavaScript变量声明提升:
在JavaScript中,函数声明与变量声明经常被JavaScript引擎隐式地提升到当前作用域的顶部。
声明语句中的赋值部分并不会被提升,只有名称被提升
函数声明的优先级高于变量,如果变量名跟函数名相同且未赋值,则函数声明会覆盖变量声明
如果函数有多个同名参数,那么最后一个参数(即使没有定义)会覆盖前面的同名参数
介绍JavaScript的原型,原型链?有什么特点?
原型:
JavaScript的所有对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是该对象的原型
JavaScript的函数对象,除了原型 [proto] 之外,还预置了 prototype 属性
当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [proto]。
原型链:
当一个对象调用的属性/方法自身不存在时,就会去自己 [proto] 关联的前辈 prototype 对象上去找
如果没找到,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链”
原型特点:
JavaScript对象是通过引用来传递的,当修改原型时,与之相关的对象也会继承这一改变
Javascript如何实现继承?
构造函数绑定:使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上
function Cat(name,color){
Animal.apply(this, arguments);
this.name = name;
this.color = color;
}
实例继承:将子对象的 prototype 指向父对象的一个实例
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
拷贝继承:如果把父对象的所有属性和方法,拷贝进子对象
function extend(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
原型继承:将子对象的 prototype 指向父对象的 prototype
function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype;}
ES6 语法糖 extends:class ColorPoint extends Point {}
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 调用父类的toString() }}
谈谈this对象的理解
this 总是指向函数的直接调用者
如果有 new 关键字,this 指向 new 出来的实例对象
在事件中,this指向触发这个事件的对象
IE下 attachEvent 中的this总是指向全局对象Window
JS阻止冒泡和取消默认事件(默认行为)
js冒泡和捕获是事件的两种行为,使用event.stopPropagation()起到阻止捕获和冒泡阶段中当前事件的进一步传播。使用event.preventDefault()可以取消默认事件
防止冒泡和捕获
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
stopPropagation也是事件对象(Event)的一个方法,作用是阻止目标元素的冒泡事件,但是会不阻止默认行为。什么是冒泡事件?如在一个按钮是绑定一个”click”事件,那么”click”事件会依次在它的父级元素中被触发 。stopPropagation就是阻止目标元素的事件冒泡到父级元素
取消默认事件
w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
preventDefault它是事件对象(Event)的一个方法,作用是取消一个目标元素的默认行为。既然是说默认行为,当然是元素必须有默认行为才能被取消,如果元素本身就没有默认行为,调用当然就无效了。什么元素有默认行为呢?如链接,提交按钮<input type=”submit”>等。当Event 对象的 cancelable为false时,表示没有默认行为,这时即使有默认行为,调用preventDefault也是不会起作用的
事件的三个阶段
捕获、目标、冒泡
介绍事件“捕获”和“冒泡”执行顺序和事件的执行次数?
按照W3C标准的事件:首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段
事件执行次数(DOM2-addEventListener):元素上绑定事件的个数
注意1:前提是事件被确实触发
注意2:事件绑定几次就算几个事件,即使类型和功能完全一样也不会“覆盖”
事件执行顺序:判断的关键是否目标元素
非目标元素:根据W3C的标准执行:捕获->目标元素->冒泡(不依据事件绑定顺序)
目标元素:依据事件绑定顺序:先绑定的事件先执行(不依据捕获冒泡标准)
最终顺序:父元素捕获->目标元素事件1->目标元素事件2->子元素捕获->子元素冒泡->父元素冒泡
注意:子元素事件执行前提 事件确实“落”到子元素布局区域上,而不是简单的具有嵌套关系
在一个DOM上同时绑定两个点击事件:一个用捕获,一个用冒泡。事件会执行几次,先执行冒泡还是捕获?
该DOM上的事件如果被触发,会执行两次(执行次数等于绑定次数)
如果该DOM是目标元素,则按事件绑定顺序执行,不区分冒泡/捕获
如果该DOM是处于事件流中的非目标元素,则先执行捕获,后执行冒泡
事件的代理/委托
事件委托是指将事件绑定目标元素的到父元素上,利用冒泡机制触发该事件
优点:
可以减少事件注册,节省大量内存占用
可以将事件应用于动态添加的子元素上
缺点: 使用不当会造成事件在不应该触发时触发
示例:
ulEl.addEventListener('click', function(e){
var target = event.target || event.srcElement;if(!!target && target.nodeName.toUpperCase() === "LI"){ console.log(target.innerHTML);}
}, false);
IE与火狐的事件机制有什么区别? 如何阻止冒泡?
IE只事件冒泡,不支持事件捕获;火狐同时支持件冒泡和事件捕获