• 不同数据类型的值的比较,是怎么转换的,有什么规则
参考回答:
• null == undefined 为什么
参考回答:
要比较相等性之前,不能将 null 和 undefined 转换成其他任何值,但 null ==
undefined 会返回 true 。ECMAScript 规范中是这样定义的。
• this 的指向 哪几种
参考回答:
默认绑定:全局环境中,this 默认绑定到 window。
隐式绑定:一般地,被直接对象所包含的函数调用时,也称为方法调用,this 隐式绑
定到该直接对象。
隐式丢失:隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到 window。
显式绑定:通过 call()、apply()、bind()方法把对象绑定到 this 上,叫做显式绑
定。
new 绑定:如果函数或者方法调用之前带有关键字 new,它就构成构造函数调用。对于
this 绑定来说,称为 new 绑定。
【1】构造函数通常不使用 return 关键字,它们通常初始化新对象,当构造函数的函
数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就
是这个新对象的值。
【2】如果构造函数使用 return 语句但没有指定返回值,或者返回一个原始值,那么
这时将忽略返回值,同时使用这个新对象作为调用结果。 【3】如果构造函数显式地使用 return 语句返回一个对象,那么调用表达式的值就是 这个对象。
• 暂停死区
参考回答:
在代码块内,使用 let、const 命令声明变量之前,该变量都是不可用的。这在语法
上,称为“暂时性死区”
• AngularJS 双向绑定原理
参考回答:
Angular 将双向绑定转换为一堆 watch 表达式,然后递归这些表达式检查是否发生过变
化,如果变了则执行相应的 watcher 函数(指 view 上的指令,如 ng-bind,ng-show
等或是{{}})。等到 model 中的值不再发生变化,也就不会再有 watcher 被触发,一
个完整的 digest 循环就完成了。
Angular 中在 view 上声明的事件指令,如:ng-click、ng-change 等,会将浏览器的
事件转发给$scope 上相应的 model 的响应函数。等待相应函数改变 model,紧接着触
发脏检查机制刷新 view。
watch 表达式:可以是一个函数、可以是$scope 上的一个属性名,也可以是一个字符
串形式的表达式。$watch 函数所监听的对象叫做 watch 表达式。watcher 函数:指在
view 上的指令(ngBind,ngShow、ngHide 等)以及{{}}表达式,他们所注册的函数。
每一个 watcher 对象都包括:监听函数,上次变化的值,获取监听表达式的方法以及
监听表达式,最后还包括是否需要使用深度对比(angular.equals())
• 写一个深度拷贝
参考回答:
function clone( obj ) {
var copy;
switch( typeof obj ) {
case "undefined":
break;
case "number":
copy = obj - 0;
break;
case "string":
copy = obj + "";
break;
case "boolean":
copy = obj;
break; case "object": //object 分为两种情况 对象(Object)和数组(Array)
if(obj === null) {
copy = null;
} else {
if( Object.prototype.toString.call(obj).slice(8, -1) === "Array") {
copy = [];
for( var i = 0 ; i < obj.length ; i++ ) {
copy.push(clone(obj[i]));
}
} else {
copy = {};
for( var j in obj) {
copy[j] = clone(obj[j]);
}
}
}
break;
default:
copy = obj;
break;
}
return copy;
}
• 有一个游戏叫做 Flappy Bird,就是一只小鸟在飞,前面是无尽的沙
漠,上下不断有钢管生成,你要躲避钢管。然后小明在玩这个游戏时候
老是卡顿甚至崩溃,说出原因(3-5 个)以及解决办法(3-5 个)
参考回答:
原因可能是:
1.内存溢出问题。
2.资源过大问题。
3.资源加载问题。
4.canvas 绘制频率问题
解决办法: 1.针对内存溢出问题,我们应该在钢管离开可视区域后,销毁钢管,让垃圾收集器回
收钢管,因为不断生成的钢管不及时清理容易导致内存溢出游戏崩溃。
2.针对资源过大问题,我们应该选择图片文件大小更小的图片格式,比如使用 webp、
png 格式的图片,因为绘制图片需要较大计算量。
3.针对资源加载问题,我们应该在可视区域之前就预加载好资源,如果在可视区域生
成钢管的话,用户的体验就认为钢管是卡顿后才生成的,不流畅。
4.针对 canvas 绘制频率问题,我们应该需要知道大部分显示器刷新频率为 60 次/s,因
此游戏的每一帧绘制间隔时间需要小于 1000/60=16.7ms,才能让用户觉得不卡顿。
(注意因为这是单机游戏,所以回答与网络无关)
• 编写代码,满足以下条件: (
1)Hero("37er");执行结果为 Hi! This
is 37er (
2)Hero("37er").kill(1).recover(30);执行结果为 Hi!
This is 37er Kill 1 bug Recover 30 bloods (
3)
Hero("37er").sleep(10).kill(2)执行结果为 Hi! This is 37er //等
待 10s 后 Kill 2 bugs //注意为 bugs (双斜线后的为提示信息,不
需要打印)
参考回答:
function Hero(name){
let o=new Object();
o.name=name;
o.time=0;
console.log("Hi! This is "+o.name);
o.kill=function(bugs) {
if(bugs==1){
console.log("Kill "+(bugs)+" bug");
}else {
setTimeout(function () {
console.log("Kill " + (bugs) + " bugs");
}, 1000 * this.time);
}
return o;
};
o.recover=function (bloods) {
console.log("Recover "+(bloods)+" bloods");
return o;
}
o.sleep=function (sleepTime) {
o.time=sleepTime;
return o;
}
return o;
}
• 什么是按需加载
参考回答:
当用户触发了动作时才加载对应的功能。触发的动作,是要看具体的业务场景而言,
包括但不限于以下几个情况:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大
小更改等。加载的文件,可以是 JS、图片、CSS、HTML 等。
• 说一下什么是 virtual dom
参考回答:
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,
插到文档当中 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树
进行比较,记录两棵树差异 把所记录的差异应用到所构建的真正的 DOM 树上,视图就
更新了。Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。
• webpack 用来干什么的
参考回答:
webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当
webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其
中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
• ant-design 优点和缺点
参考回答:
优点:组件非常全面,样式效果也都比较不错。
缺点:框架自定义程度低,默认 UI 风格修改困难。
• JS 中继承实现的几种方式,
参考回答:
1、原型链继承,将父类的实例作为子类的原型,他的特点是实例是子类的实例也是父
类的实例,父类新增的原型方法/属性,子类都能够访问,并且原型链继承简单易于实
现,缺点是来自原型对象的所有属性被所有实例共享,无法实现多继承,无法向父类
构造函数传参。
2、构造继承,使用父类的构造函数来增强子类实例,即复制父类的实例属性给子类,
构造继承可以向父类传递参数,可以实现多继承,通过 call 多个父类对象。但是构造
继承只能继承父类的实例属性和方法,不能继承原型属性和方法,无法实现函数服
用,每个子类都有父类实例函数的副本,影响性能 3、实例继承,为父类实例添加新特性,作为子类实例返回,实例继承的特点是不限制
调用方法,不管是 new 子类()还是子类()返回的对象具有相同的效果,缺点是实
例是父类的实例,不是子类的实例,不支持多继承
4、拷贝继承:特点:支持多继承,缺点:效率较低,内存占用高(因为要拷贝父类的
属性)无法获取父类不可枚举的方法(不可枚举方法,不能使用 for in 访问到)
5、组合继承:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父
类实例作为子类原型,实现函数复用
6、寄生组合继承:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构
造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
• 写一个函数,第一秒打印 1,第二秒打印 2
参考回答:
两个方法,第一个是用 let 块级作用域
for(let i=0;i<5;i++){
setTimeout(function(){
console.log(i)
},1000*i)
}
第二个方法闭包
for(var i=0;i<5;i++){
(function(i){
setTimeout(function(){
console.log(i)
},1000*i)
})(i)
}
• Vue 的生命周期
参考回答:
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载
Dom、渲染→更新→渲染、销毁等一系列过程,我们称这是 Vue 的生命周期。通俗说就
是 Vue 实例从创建到销毁的过程,就是生命周期。
每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运
行中、销毁。
实例、组件通过 new Vue() 创建出来之后会初始化事件和生命周期,然后就会执行
beforeCreate 钩子函数,这个时候,数据还没有挂载呢,只是一个空壳,无法访问到
数据和真实的 dom,一般不做操作
挂载数据,绑定事件等等,然后执行 created 函数,这个时候已经可以使用到数据,
也可以更改数据,在这里更改数据不会触发 updated 函数,在这里可以在渲染前倒数第
二次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取 接下来开始找实例或者组件对应的模板,编译模板为虚拟 dom 放入到 render 函数中准
备渲染,然后执行 beforeMount 钩子函数,在这个函数中虚拟 dom 已经创建完成,马
上就要渲染,在这里也可以更改数据,不会触发 updated,在这里可以在渲染前最后一
次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
接下来开始 render,渲染出真实 dom,然后执行 mounted 钩子函数,此时,组件已经
出现在页面中,数据、真实 dom 都已经处理好了,事件都已经挂载好了,可以在这里操
作真实 dom 等事情...
当组件或实例的数据更改之后,会立即执行 beforeUpdate,然后 Vue 的虚拟 dom 机制
会重新构建虚拟 dom 与上一次的虚拟 dom 树利用 diff 算法进行对比之后重新渲染,一
般不做什么事儿
当更新完成后,执行 updated,数据已经更改完成,dom 也重新 render 完成,可以操
作更新后的虚拟 dom
当经过某种途径调用$destroy 方法后,立即执行 beforeDestroy,一般在这里做一些
善后工作,例如清除计时器、清除非指令绑定的事件等等
组件的数据绑定、监听...去掉后只剩下 dom 空壳,这个时候,执行 destroyed,在这
里做善后工作也可以
• 简单介绍一下 symbol
参考回答:
Symbol 是 ES6 的新增属性,代表用给定名称作为唯一标识,这种类型的值可以这样创
建,let id=symbol(“id”)
Symbl 确保唯一,即使采用相同的名称,也会产生不同的值,我们创建一个字段,仅为
知道对应 symbol 的人能访问,使用 symbol 很有用,symbol 并不是 100%隐藏,有内置
方法 Object.getOwnPropertySymbols(obj)可以获得所有的 symbol。
也有一个方法 Reflect.ownKeys(obj)返回对象所有的键,包括 symbol。
所以并不是真正隐藏。但大多数库内置方法和语法结构遵循通用约定他们是隐藏的。
• 什么是事件监听
参考回答:
addEventListener()方法,用于向指定元素添加事件句柄,它可以更简单的控制事
件,语法为
element.addEventListener(event, function, useCapture);
第一个参数是事件的类型(如 "click" 或 "mousedown").
第二个参数是事件触发后调用的函数。
第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。
事件传递有两种方式,冒泡和捕获
事件传递定义了元素事件触发的顺序,如果你将 P 元素插入到 div 元素中,用户点击 P
元素,
在冒泡中,内部元素先被触发,然后再触发外部元素,
捕获中,外部元素先被触发,在触发内部元素。
• 介绍一下 promise,及其底层如何实现
参考回答:
Promise 是一个对象,保存着未来将要结束的事件,她有两个特征:
1、对象的状态不受外部影响,Promise 对象代表一个异步操作,有三种状态,pending
进行中,fulfilled 已成功,rejected 已失败,只有异步操作的结果,才可以决定当
前是哪一种状态,任何其他操作都无法改变这个状态,这也就是 promise 名字的由来
2、一旦状态改变,就不会再变,promise 对象状态改变只有两种可能,从 pending 改
到 fulfilled 或者从 pending 改到 rejected,只要这两种情况发生,状态就凝固了,
不会再改变,这个时候就称为定型 resolved,
Promise 的基本用法,
let promise1 = new Promise(function(resolve,reject){
setTimeout(function(){
resolve('ok')
},1000)
})
promise1.then(function success(val){
console.log(val)
})
最简单代码实现 promise
class PromiseM {
constructor (process) {
this.status = 'pending'
this.msg = ''
process(this.resolve.bind(this), this.reject.bind(this))
return this
}
resolve (val) {
this.status = 'fulfilled'
this.msg = val
}
reject (err) {
this.status = 'rejected'
this.msg = err
}
then (fufilled, reject) {
if(this.status === 'fulfilled') {
fufilled(this.msg)
}
if(this.status === 'rejected') {
reject(this.msg)
}
}
} //测试代码
var mm=new PromiseM(function(resolve,reject){
resolve('123');
});
mm.then(function(success){
console.log(success);
},function(){
console.log('fail!');
});
• 说说 C++,Java,JavaScript 这三种语言的区别
参考回答:
从静态类型还是动态类型来看
静态类型,编译的时候就能够知道每个变量的类型,编程的时候也需要给定类型,如
Java 中的整型 int,浮点型 float 等。C、C++、Java 都属于静态类型语言。
动态类型,运行的时候才知道每个变量的类型,编程的时候无需显示指定类型,如
JavaScript 中的 var、PHP 中的$。JavaScript、Ruby、Python 都属于动态类型语言。
静态类型还是动态类型对语言的性能有很大影响。
对于静态类型,在编译后会大量利用已知类型的优势,如 int 类型,占用 4 个字节,
编译后的代码就可以用内存地址加偏移量的方法存取变量,而地址加偏移量的算法汇
编很容易实现。
对于动态类型,会当做字符串通通存下来,之后存取就用字符串匹配。
从编译型还是解释型来看
编译型语言,像 C、C++,需要编译器编译成本地可执行程序后才能运行,由开发人员
在编写完成后手动实施。用户只使用这些编译好的本地代码,这些本地代码由系统加
载器执行,由操作系统的 CPU 直接执行,无需其他额外的虚拟机等。
源代码=》抽象语法树=》中间表示=》本地代码
解释性语言,像 JavaScript、Python,开发语言写好后直接将代码交给用户,用户使
用脚本解释器将脚本文件解释执行。对于脚本语言,没有开发人员的编译过程,当
然,也不绝对。
源代码=》抽象语法树=》解释器解释执行。
对于 JavaScript,随着 Java 虚拟机 JIT 技术的引入,工作方式也发生了改变。可以将
抽象语法树转成中间表示(字节码),再转成本地代码,如 JavaScriptCore,这样可
以大大提高执行效率。也可以从抽象语法树直接转成本地代码,如 V8
Java 语言,分为两个阶段。首先像 C++语言一样,经过编译器编译。和 C++的不同,
C++编译生成本地代码,Java 编译后,生成字节码,字节码与平台无关。第二阶段,由
Java 的运行环境也就是 Java 虚拟机运行字节码,使用解释器执行这些代码。一般情况
下,Java 虚拟机都引入了 JIT 技术,将字节码转换成本地代码来提高执行效率。
注意,在上述情况中,编译器的编译过程没有时间要求,所以编译器可以做大量的代
码优化措施。
对于 JavaScript 与 Java 它们还有的不同: 对于 Java,Java 语言将源代码编译成字节码,这个同执行阶段是分开的。也就是从源
代码到抽象语法树到字节码这段时间的长短是无所谓的。
对于 JavaScript,这些都是在网页和 JavaScript 文件下载后同执行阶段一起在网页的
加载和渲染过程中实施的,所以对于它们的处理时间有严格要求。
• JS 原型链,原型链的顶端是什么?Object 的原型是什么?Object 的原
型的原型是什么?在数组原型链上实现删除数组重复数据的方法
参考回答:
能够把这个讲清楚弄明白是一件很困难的事,
首先明白原型是什么,在 ES6 之前,JS 没有类和继承的概念,JS 是通过原型来实现继
承的,在 JS 中一个构造函数默认带有一个 prototype 属性,这个的属性值是一个对
象,同时这个 prototype 对象自带有一个 constructor 属性,这个属性指向这个构造
函数,同时每一个实例都会有一个_proto_属性指向这个 prototype 对象,我们可以把
这个叫做隐式原型,我们在使用一个实例的方法的时候,会先检查这个实例中是否有
这个方法,没有的话就会检查这个 prototype 对象是否有这个方法,
基于这个规则,如果让原型对象指向另一个类型的实例,即
constructor1.protoytpe=instance2,这时候如果试图引用 constructor1 构造的实例
instance1 的某个属性 p1,
首先会在 instance1 内部属性中找一遍,
接着会在 instance1._proto_(constructor1.prototype)即是 instance2 中寻找 p1
搜寻轨迹:
instance1->instance2->constructor2.prototype……->Object.prototype;这即是原
型链,原型链顶端是 Object.prototype
补充学习:
每个函数都有一个 prototype 属性,这个属性指向了一个对象,这个对象正是调用该
函数而创建的实例的原型,那么什么是原型呢,可以这样理解,每一个 JavaScript 对
象在创建的时候就会预制管理另一个对象,这个对象就是我们所说的原型,每一个对
象都会从原型继承属性,如图
那么怎么表示实例与实例原型的关系呢,这时候就要用到第二个属性_proto_
这是每一个 JS 对象都会有的一个属性,指向这个对象的原型,如图:
既然实例对象和构造函数都可以指向原型,那么原型是否有属性指向构造函数或者实
例呢,指向实例是没有的,因为一个构造函数可以生成多个实例,但是原型有属性可
以直接指向构造函数,通过 constructor 即可
接下来讲解实例和原型的关系:
当读取实例的属性时,如果找不到,就会查找与对象相关的原型中的属性,如果还查
不到,就去找原型的原型,一直找到最顶层,那么原型的原型是什么呢,首先,原型
也是一个对象,既然是对象,我们就可以通过构造函数的方式创建它,所以原型对象
就是通过 Object 构造函数生成的,如图:
那么 Object.prototype 的原型呢,我们可以打印
console.log(Object.prototype.__proto__ === null),返回 true
null 表示没有对象,即该处不应有值,所以 Object.prototype 没有原型,如图:
图中这条蓝色的线即是原型链,
最后补充三点:
constructor:
function Person(){
}
var person = new Person();
console.log(Person === person.constructor);
原本 person 中没有 constructor 属性,当不能读取到 constructor 属性时,会从
person 的原型中读取,所以指向构造函数 Person
__proto__:
绝大部分浏览器支持这个非标准的方法访问原型,然而它并不存在与
Person.prototype 中,实际上它来自 Object.prototype,当使用 obj.__proto__时,
可以理解为返回来 Object.getPrototype(obj)
继承: 前面说到,每个对象都会从原型继承属性,但是引用《你不知道的 JS》中的话,继承
意味着复制操作,然而 JS 默认不会复制对象的属性,相反,JS 只是在两个对象之间创
建一个关联,这样子一个对象就可以通过委托访问另一个对象的属性和函数,所以与
其叫继承,叫委托更合适。
• 什么是 js 的闭包?有什么作用,用闭包写个单例模式
参考回答:
MDN 对闭包的定义是:闭包是指那些能够访问自由变量的函数,自由变量是指在函数中
使用的,但既不是函数参数又不是函数的局部变量的变量,由此可以看出,闭包=函数
+函数能够访问的自由变量,所以从技术的角度讲,所有 JS 函数都是闭包,但是这是
理论上的闭包,还有一个实践角度上的闭包,从实践角度上来说,只有满足 1、即使创
建它的上下文已经销毁,它仍然存在,2、在代码中引入了自由变量,才称为闭包
闭包的应用:
模仿块级作用域。2、保存外部函数的变量。3、封装私有变量
单例模式:
var Singleton = (function(){
var instance;
var CreateSingleton = function (name) {
this.name = name;
if(instance) {
return instance;
}
// 打印实例名字
this.getName();
// instance = this;
// return instance;
return instance = this;
}
// 获取实例的名字
CreateSingleton.prototype.getName = function() {
console.log(this.name)
}
return CreateSingleton;
})();
// 创建实例对象 1
var a = new Singleton('a');
// 创建实例对象 2
var b = new Singleton('b');
console.log(a===b);
• promise+Generator+Async 的使用 参考回答:
Promise
解决的问题:回调地狱
Promise 规范:
promise 有三种状态,等待(pending)、已完成(fulfilled/resolved)、已拒绝
(rejected).Promise 的状态只能从“等待”转到“完成”或者“拒绝”,不能逆向
转换,同时“完成”和“拒绝”也不能相互转换.
promise 必须提供一个 then 方法以访问其当前值、终值和据因。
promise.then(resolve, reject),resolve 和 reject 都是可选参数。如果 resolve
或 reject 不是函数,其必须被忽略.
then 方法必须返回一个 promise 对象.
使用:
实例化 promise 对象需要传入函数(包含两个参数),resolve 和 reject,内部确定状
态.resolve 和 reject 函数可以传入参数在回调函数中使用.
resolve 和 reject 都是函数,传入的参数在 then 的回调函数中接收.
var promise = new Promise(function(resolve, reject) {
setTimeout(function(){
resolve('好哈哈哈哈');
});
});
promise.then(function(val){
console.log(val)
})
then 接收两个函数,分别对应 resolve 和 reject 状态的回调,函数中接收实例化时传入
的参数.
promise.then(val=>{
//resolved
},reason=>{
//rejected
})
catch 相当于.then(null, rejection)
当 then 中没有传入 rejection 时,错误会冒泡进入 catch 函数中,若传入了 rejection,
则错误会被 rejection 捕获,而且不会进入 catch.此外,then 中的回调函数中发生的错
误只会在下一级的 then 中被捕获,不会影响该 promise 的状态.
new Promise((resolve,reject)=>{
throw new Error('错误')
}).then(null,(err)=>{
console.log(err,1);//此处捕获
}).catch((err)=>{
console.log(err,2);
});
// 对比
new Promise((resolve,reject)=>{ throw new Error('错误')
}).then(null,null).catch((err)=>{
console.log(err,2);//此处捕获
});
// 错误示例
new Promise((resolve,reject)=>{
resolve('正常');
}).then((val)=>{
throw new Error('回调函数中错误')
},(err)=>{
console.log(err,1);
}).then(null,(err)=>{
console.log(err,2);//此处捕获,也可用 catch
});
两者不等价的情况:
此时,catch 捕获的并不是 p1 的错误,而是 p2 的错误,
p1().then(res=>{
return p2()//p2 返回一个 promise 对象
}).catch(err=> console.log(err))
一个错误捕获的错误用例:
该函数调用中即使发生了错误依然会进入 then 中的 resolve 的回调函数,因为函数 p1
中实例化 promise 对象时已经调用了 catch,若发生错误会进入 catch 中,此时会返回一
个新的 promise,因此即使发生错误依然会进入 p1 函数的 then 链中的 resolve 回调函
数.
function p1(val){
return new Promise((resolve,reject)=>{
if(val){
var len = val.length;//传入 null 会发生错误,进入 catch 捕获错
resolve(len);
}else{
reject();
}
}).catch((err)=>{
console.log(err)
})
};
p1(null).then((len)=>{
console.log(len,'resolved');
},()=>{
console.log('rejected');
}).catch((err)=>{ console.log(err,'catch');
})
Promise 回调链:
promise 能够在回调函数里面使用 return 和 throw, 所以在 then 中可以 return 出
一个 promise 对象或其他值,也可以 throw 出一个错误对象,但如果没有 return,将
默认返回 undefined,那么后面的 then 中的回调参数接收到的将是 undefined.
function p1(val){
return new Promise((resolve,reject)=>{
val==1?resolve(1):reject()
})
};
function p2(val){
return new Promise((resolve,reject)=>{
val==2?resolve(2):reject();
})
};
let promimse = new Promise(function(resolve,reject){
resolve(1)
})
.then(function(data1) {
return p1(data1)//如果去掉 return,则返回 undefined 而不是 p1 的返回值,
会导致报错
})
.then(function(data2){
return p2(data2+1)
})
.then(res=>console.log(res))
Generator 函数:
generator 函数使用:
1、分段执行,可以暂停
2、可以控制阶段和每个阶段的返回值
3、可以知道是否执行到结尾
function* g() {
var o = 1;
yield o++;
yield o++;
}
var gen = g();
console.log(gen.next()); // Object {value: 1, done: false}
var xxx = g();
console.log(gen.next()); // Object {value: 2, done: false}
console.log(xxx.next()); // Object {value: 1, done: false}
console.log(gen.next()); // Object {value: undefined, done: true} generator 和异步控制:
利用 Generator 函数的暂停执行的效果,可以把异步操作写在 yield 语句里面,等到
调用 next 方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的
后续操作可以放在 yield 语句下面,反正要等到调用 next 方法时再执行。所以,
Generator 函数的一个重要实际意义就是用来处理异步操作,改写回调函数。
async 和异步:
用法:
async 表示这是一个 async 函数,await 只能用在这个函数里面。
await 表示在这里等待异步操作返回结果,再继续执行。
await 后一般是一个 promise 对象
示例:async 用于定义一个异步函数,该函数返回一个 Promise。
如果 async 函数返回的是一个同步的值,这个值将被包装成一个理解 resolve 的
Promise,等同于 return Promise.resolve(value)。
await 用于一个异步操作之前,表示要“等待”这个异步操作的返回值。await 也可以
用于一个同步的值。
let timer = async function timer(){
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve('500');
},500);
});
}
timer().then(result => {
console.log(result); //500
}).catch(err => {
console.log(err.message);
});
//返回一个同步的值
let sayHi = async function sayHi(){
let hi = await 'hello world';
return hi; //等同于 return Promise.resolve(hi);
}
sayHi().then(result => {
console.log(result);
});
• 事件委托以及冒泡原理。
参考回答:
事件委托是利用冒泡阶段的运行机制来实现的,就是把一个元素响应事件的函数委托
到另一个元素,一般是把一组元素的事件委托到他的父元素上,委托的优点是 减少内存消耗,节约效率
动态绑定事件
事件冒泡,就是元素自身的事件被触发后,如果父元素有相同的事件,如 onclick 事
件,那么元素本身的触发状态就会传递,也就是冒到父元素,父元素的相同事件也会
一级一级根据嵌套关系向外触发,直到 document/window,冒泡过程结束。
• 写个函数,可以转化下划线命名到驼峰命名
参考回答:
public static String UnderlineToHump(String para){
StringBuilder result=new StringBuilder();
String a[]=para.split("_");
for(String s:a){
if(result.length()==0){
result.append(s.toLowerCase());
}else{
result.append(s.substring(0, 1).toUpperCase());
result.append(s.substring(1).toLowerCase());
}
}
return result.toString();
}
}
• 深浅拷贝的区别和实现
参考回答:
数组的浅拷贝:
如果是数组,我们可以利用数组的一些方法,比如 slice,concat 方法返回一个新数
组的特性来实现拷贝,但假如数组嵌套了对象或者数组的话,使用 concat 方法克隆并
不完整,如果数组元素是基本类型,就会拷贝一份,互不影响,而如果是对象或数
组,就会只拷贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会
发生变化,我们把这种复制引用的拷贝方法称为浅拷贝,
深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也互相分离,修改一个对
象的属性,不会影响另一个
如何深拷贝一个数组
1、这里介绍一个技巧,不仅适用于数组还适用于对象!那就是:
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]
var new_arr = JSON.parse( JSON.stringify(arr) );
console.log(new_arr);
原理是 JOSN 对象中的 stringify 可以把一个 js 对象序列化为一个 JSON 字符串,
parse 可以把 JSON 字符串反序列化为一个 js 对象,通过这两个方法,也可以实现对象
的深复制。 但是这个方法不能够拷贝函数
浅拷贝的实现:
以上三个方法 concat,slice ,JSON.stringify 都是技巧类,根据实际项目情况选择使
用,我们可以思考下如何实现一个对象或数组的浅拷贝,遍历对象,然后把属性和属
性值都放在一个新的对象里即可
var shallowCopy = function(obj) {
// 只拷贝对象
if (typeof obj !== 'object') return;
// 根据 obj 的类型判断是新建一个数组还是对象
var newObj = obj instanceof Array ? [] : {};
// 遍历 obj,并且判断是 obj 的属性才拷贝
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
深拷贝的实现
那如何实现一个深拷贝呢?说起来也好简单,我们在拷贝的时候判断一下属性值的类
型,如果是对象,我们递归调用深拷贝函数不就好了~
var deepCopy = function(obj) {
if (typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) :
obj[key];
}
}
return newObj;
}
• JS 中 string 的 startwith 和 indexof 两种方法的区别
参考回答: JS 中 startwith 函数,其参数有 3 个,stringObj,要搜索的字符串对象,str,搜索的
字符串,position,可选,从哪个位置开始搜索,如果以 position 开始的字符串以搜
索字符串开头,则返回 true,否则返回 false
Indexof 函数,indexof 函数可返回某个指定字符串在字符串中首次出现的位置。
• JS 字符串转数字的方法
参考回答:
通过函数 parseInt(),可解析一个字符串,并返回一个整数,语法为 parseInt
(string ,radix)
string:被解析的字符串
radix:表示要解析的数字的基数,默认是十进制,如果 radix<2 或>36,则返回 NaN
• let const var 的区别 ,什么是块级作用域,如何用 ES5 的方法实现
块级作用域(立即执行函数),ES6 呢
参考回答:
提起这三个最明显的区别是 var 声明的变量是全局或者整个函数块的,而 let,const
声明的变量是块级的变量,var 声明的变量存在变量提升,let,const 不存在,let 声
明的变量允许重新赋值,const 不允许。
• ES6 箭头函数的特性
参考回答:
ES6 增加了箭头函数,基本语法为
let func = value => value;
相当于
let func = function (value) {
return value;
};
箭头函数与普通函数的区别在于:
1、箭头函数没有 this,所以需要通过查找作用域链来确定 this 的值,这就意味着如
果箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 this,
2、箭头函数没有自己的 arguments 对象,但是可以访问外围函数的 arguments 对象
3、不能通过 new 关键字调用,同样也没有 new.target 值和原型
• setTimeout 和 Promise 的执行顺序
参考回答:
首先我们来看这样一道题:
setTimeout(function() { console.log(1)
}, 0);
new Promise(function(resolve, reject) {
console.log(2)
for (var i = 0; i < 10000; i++) {
if(i === 10) {console.log(10)}
i == 9999 && resolve();
}
console.log(3)
}).then(function() {
console.log(4)
})
console.log(5);
输出答案为 2 10 3 5 4 1
要先弄清楚 settimeout(fun,0)何时执行,promise 何时执行,then 何时执行
settimeout 这种异步操作的回调,只有主线程中没有执行任何同步代码的前提下,才
会执行异步回调,而 settimeout(fun,0)表示立刻执行,也就是用来改变任务的执行
顺序,要求浏览器尽可能快的进行回调
promise 何时执行,由上图可知 promise 新建后立即执行,所以 promise 构造函数里代
码同步执行的,
then 方法指向的回调将在当前脚本所有同步任务执行完成后执行,
那么 then 为什么比 settimeout 执行的早呢,因为 settimeout(fun,0)不是真的立即
执行,
经过测试得出结论:执行顺序为:同步执行的代码-》promise.then->settimeout
• 有了解过事件模型吗,DOM0 级和 DOM2 级有什么区别,DOM 的分级是什
么
参考回答:
JSDOM 事件流存在如下三个阶段:
事件捕获阶段
处于目标阶段
事件冒泡阶段
JSDOM 标准事件流的触发的先后顺序为:先捕获再冒泡,点击 DOM 节点时,事件传播顺
序:事件捕获阶段,从上往下传播,然后到达事件目标节点,最后是冒泡阶段,从下
往上传播
DOM 节点添加事件监听方法 addEventListener,中参数 capture 可以指定该监听是添
加在事件捕获阶段还是事件冒泡阶段,为 false 是事件冒泡,为 true 是事件捕获,并
非所有的事件都支持冒泡,比如 focus,blur 等等,我们可以通过 event.bubbles 来
判断
事件模型有三个常用方法:
event.stopPropagation:阻止捕获和冒泡阶段中,当前事件的进一步传播,
event.stopImmediatePropagetion,阻止调用相同事件的其他侦听器, event.preventDefault,取消该事件(假如事件是可取消的)而不停止事件的进一步
传播,
event.target:指向触发事件的元素,在事件冒泡过程中这个值不变
event.currentTarget = this,时间帮顶的当前元素,只有被点击时目标元素的
target 才会等于 currentTarget,
最后,对于执行顺序的问题,如果 DOM 节点同时绑定了两个事件监听函数,一个用于
捕获,一个用于冒泡,那么两个事件的执行顺序真的是先捕获在冒泡吗,答案是否定
的,绑定在被点击元素的事件是按照代码添加顺序执行的,其他函数是先捕获再冒泡
• 平时是怎么调试 JS 的
参考回答:
一般用 Chrome 自带的控制台
• JS 的基本数据类型有哪些,基本数据类型和引用数据类型的区别,NaN
是什么的缩写,JS 的作用域类型,undefined==null 返回的结果是什
么,undefined 与 null 的区别在哪,写一个函数判断变量类型
参考回答:
JS 的基本数据类型有字符串,数字,布尔,数组,对象,Null,Undefined,基本数据
类型是按值访问的,也就是说我们可以操作保存在变量中的实际的值,
基本数据类型和引用数据类型的区别如下: