第2章 第9节 JavaScript(四)

推荐给朋友

● 知道哪些ES6,ES7的语法

参考回答:

promise,await/async,let、const、块级作用域、箭头函数

● promise和await/async的关系

参考回答:

都是异步编程的解决方案

● 问了一段js代码,输出结果是什么

● js的数据类型

参考回答:

字符串,数字,布尔,数组,null,Undefined,symbol,对象。

● js加载过程阻塞,解决方法。

参考回答:

指定script标签的async属性。

如果async="async",脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行)

如果不使用async 且 defer="defer":脚本将在页面完成解析时执行

● js对象类型,基本对象类型以及引用对象类型的区别

参考回答:

分为基本对象类型和引用对象类型

基本数据类型:按值访问,可操作保存在变量中的实际的值。基本类型值指的是简单的数据段。基本数据类型有这六种:undefined、null、string、number、boolean、symbol。

引用类型:当复制保存着对象的某个变量时,操作的是对象的引用,但在为对象添加属性时,操作的是实际的对象。引用类型值指那些可能为多个值构成的对象。

引用类型有这几种:Object、Array、RegExp、Date、Function、特殊的基本包装类型(String、Number、Boolean)以及单体内置对象(Global、Math)。

● JavaScript中的轮播实现原理?假如一个页面上有两个轮播,你会怎么实现?

参考回答:

图片轮播的原理就是图片排成一行,然后准备一个只有一张图片大小的容器,对这个容器设置超出部分隐藏,在控制定时器来让这些图片整体左移或右移,这样呈现出来的效果就是图片在轮播了。

如果有两个轮播,可封装一个轮播组件,供两处调用

● 怎么实现一个计算一年中有多少周?

参考回答:

首先你得知道是不是闰年,也就是一年是365还是366.
其次你得知道当年1月1号是周几。假如是周五,一年365天把1号 2号3号减去,也就是把第一个不到一周的天数减去等于362
还得知道最后一天是周几,加入是周五,需要把周一到周五减去,也就是362-5=357.正常情况 357这个数计算出来是7的倍数。357/7=51 。即为周数。

● 面向对象的继承方式

参考回答:

原型链继承

核心: 将父类的实例作为子类的原型

特点:

非常纯粹的继承关系,实例是子类的实例,也是父类的实例

父类新增原型方法/原型属性,子类都能访问到

简单,易于实现

缺点:

要想为子类新增属性和方法,不能放到构造器中

无法实现多继承

来自原型对象的所有属性被所有实例共享

创建子类实例时,无法向父类构造函数传参


构造继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

特点:

解决了子类实例共享父类引用属性的问题

创建子类实例时,可以向父类传递参数

可以实现多继承(call多个父类对象)

缺点:

实例并不是父类的实例,只是子类的实例

只能继承父类的实例属性和方法,不能继承原型属性/方法

无法实现函数复用,每个子类都有父类实例函数的副本,影响性能


实例继承

核心:为父类实例添加新特性,作为子类实例返回

特点:

不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果

缺点:

实例是父类的实例,不是子类的实例

不支持多继承


拷贝继承

特点:

支持多继承

缺点:

效率较低,内存占用高(因为要拷贝父类的属性)


组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

特点:

可以继承实例属性/方法,也可以继承原型属性/方法

既是子类的实例,也是父类的实例

不存在引用属性共享问题

可传参

函数可复用


寄生组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

参考https://www.cnblogs.com/humin/p/4556820.html

● JS的数据类型

参考回答:

字符串,数字,布尔,数组,null,Undefined,symbol,对象。

● 引用类型常见的对象

参考回答:

Object、Array、RegExp、Date、Function、特殊的基本包装类型(String、Number、Boolean)以及单体内置对象(Global、Math)等

● es6的常用

参考回答:

promise,await/async,let、const、块级作用域、箭头函数

● class

参考回答:

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

● 口述数组去重

参考回答:

法一:indexOf循环去重

法二:ES6 Set去重;Array.from(new Set(array))

法三:Object 键值对去重;把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。

● 继承

参考回答:

原型链继承

核心: 将父类的实例作为子类的原型

特点:

非常纯粹的继承关系,实例是子类的实例,也是父类的实例

父类新增原型方法/原型属性,子类都能访问到

简单,易于实现

缺点:

要想为子类新增属性和方法,不能放到构造器中

无法实现多继承

来自原型对象的所有属性被所有实例共享

创建子类实例时,无法向父类构造函数传参


构造继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

特点:

解决了子类实例共享父类引用属性的问题

创建子类实例时,可以向父类传递参数

可以实现多继承(call多个父类对象)

缺点:

实例并不是父类的实例,只是子类的实例

只能继承父类的实例属性和方法,不能继承原型属性/方法

无法实现函数复用,每个子类都有父类实例函数的副本,影响性能


实例继承

核心:为父类实例添加新特性,作为子类实例返回

特点:

不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果

缺点:

实例是父类的实例,不是子类的实例

不支持多继承


拷贝继承

特点:

支持多继承

缺点:

效率较低,内存占用高(因为要拷贝父类的属性)


组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

特点:

可以继承实例属性/方法,也可以继承原型属性/方法

既是子类的实例,也是父类的实例

不存在引用属性共享问题

可传参

函数可复用


寄生组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

参考https://www.cnblogs.com/humin/p/4556820.html

● call和apply的区别

参考回答:

apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。

call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。

● es6的常用特性

参考回答:

promise,await/async,let、const、块级作用域、箭头函数

● 箭头函数和function有什么区别

参考回答:

箭头函数根本就没有绑定自己的this,在箭头函数中调用 this 时,仅仅是简单的沿着作用域链向上寻找,找到最近的一个 this 拿来使用

● new操作符原理

参考回答:

1. 创建一个类的实例:创建一个空对象obj,然后把这个空对象的__proto__设置为构造函数的prototype。

2. 初始化实例:构造函数被传入参数并调用,关键字this被设定指向该实例obj。

3. 返回实例obj。

● bind,apply,call

参考回答:

apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。

call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。

bind除了返回是函数以外,它的参数和call一样。

● bind和apply的区别

参考回答:

返回不同:bind返回是函数

参数不同:apply(A, arguments),bind(A, args1,args2)

● 数组的去重

参考回答:

法一:indexOf循环去重

法二:ES6 Set去重;Array.from(new Set(array))

法三:Object 键值对去重;把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。

● 闭包

参考回答:

(1)什么是闭包:

闭包是指有权访问另外一个函数作用域中的变量的函数。

闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配。当在一个函数内定义另外一个函数就会产生闭包。

(2)为什么要用:

匿名自执行函数:我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。除了每次使用变量都是用var关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,可以用闭包。

结果缓存:我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

● promise实现

参考回答:

Promise实现如下
function Promise(fn) {
var state = 'pending',
value = null,
callbacks = [];
this.then = function (onFulfilled, onRejected) {
return new Promise(function (resolve, reject) {
handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
};
function handle(callback) {
if (state === 'pending') {
callbacks.push(callback);
return;
}
var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected,
ret;
if (cb === null) {
cb = state === 'fulfilled' ? callback.resolve : callback.reject;
cb(value);
return;
}
ret = cb(value);
callback.resolve(ret);
}
function resolve(newValue) {
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (typeof then === 'function') {
then.call(newValue, resolve, reject);
return;
}
}
state = 'fulfilled';
value = newValue;
execute();
}
function reject(reason) {
state = 'rejected';
value = reason;
execute();
}
function execute() {
setTimeout(function () {
callbacks.forEach(function (callback) {
handle(callback);
});
}, 0);
}
fn(resolve, reject);
}

● assign的深拷贝

参考回答:

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;
}

● 说promise,没有promise怎么办

参考回答:

没有promise,可以用回调函数代替

● 事件委托

参考回答:

把一个元素响应事件(click、keydown......)的函数委托到另一个元素;

优点:减少内存消耗、动态绑定事件。

●  怎么用原生的js实现jquery的一个特定方法

● 箭头函数和function的区别

参考回答:

箭头函数根本就没有绑定自己的this,在箭头函数中调用 this 时,仅仅是简单的沿着作用域链向上寻找,找到最近的一个 this 拿来使用

● arguments

参考回答:

arguments是类数组对象,有length属性,不能调用数组方法

可用Array.from()转换

● 箭头函数获取arguments

参考回答:

可用…rest参数获取

● Promise

参考回答:

Promise对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。

f1().then(f2);

一个promise可能有三种状态:等待(pending)、已完成(resolved,又称fulfilled)、已拒绝(rejected)。

promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致。

then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。

● 模块化开发(require)

● 事件代理

参考回答:

事件代理是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件代理,代理它们父级代为执行事件。

● Eventloop

参考回答:

任务队列中,在每一次事件循环中,macrotask只会提取一个执行,而microtask会一直提取,直到microsoft队列为空为止。
也就是说如果某个microtask任务被推入到执行中,那么当主线程任务执行完成后,会循环调用该队列任务中的下一个任务来执行,直到该任务队列到最后一个任务为止。而事件循环每次只会入栈一个macrotask,主线程执行完成该任务后又会检查microtasks队列并完成里面的所有任务后再执行macrotask的任务。

macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks: process.nextTick, Promise, MutationObserver