一. 普通函数和箭头函数的区别
1. 箭头函数没有 functioin 关键字
2. 箭头函数作为匿名函数,是不能作为构造函数的,不能使用new操作符进行实例
3. 箭头函数不能绑定arguments,取而代之的是rest,例如:
es5:
function a() {
console.log(arguments)
}
a(1,2,3,4)
es6:
let a = (...b) => {
console.log(b)
}
a(1,2,3,4)
4. 普通函数的this指向调用它的那个对象
var length = 10
function fn() {
console.log(this.length)
}
var t = {
length: 5,
methid: function(fn) {
fn() // 此时调用,方法内部this指向的是window,所以第一次打印的是10
arguments[0]() // 此时调用,方法内部this指向的是arguments对象,所以打印的是arguments的长度
}
}
t.methid(fn, 1) // 结果是0和1
6. 箭头函数没有原型属性
7. 箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()
二.经典this面试题
var x = 3;
var y = 4;
var obj = {
x: 1,
y: 6,
getX: function() {
var x =5;
return function() {
return this.x;
}();
},
getY: function() {
var y =7;
return this.y;
}
}
console.log(obj.getX())
console.log(obj.getY())
var name="the window";
var object={
name:"My Object",
getName:function(){
return this.name;
}
}
object.getName();
(object.getName)();
(object.getName=object.getName)(); //"the window",函数赋值会改变内部this的指向,这也是为什么需要在 React 类组件中为事件处理程序绑定this的原因;
var a=10;
var obt={
a:20,
fn:function(){
var a=30;
console.log(this.a)
}
}
obt.fn();
obt.fn.call();
(obt.fn)();
(obt.fn,obt.fn)(); // 和赋值操作差不多,this不能维持,所以返回全局作用域的a
new obt.fn();
function a(xx){
this.x = xx;
return this
};
var x = a(5);
var y = a(6);
console.log(x.x)
console.log(y.x)
三. apply、call、bind
1.共同点:
1-1: call 、apply 和bind都是为了改变函数体内部 this 的指向
2.区别:接受参数的方式不太一样
2-1:func.call(this, arg1, arg2); func.bind(this, arg1, arg2);
2-2:func.apply(this, [arg1, arg2])
2-3:bind返回的是函数,call和apply会立即执行
3. 实例
3-1:数组追加
var array1 = [12 , "foo" , {name:"Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
console.log(array1)
3-2:获取数组中的最大值和最小值
var numbers = [5, 458 , 120 , -215 ];
var maxNum = Math.max.apply(Math, numbers)
var minNum = Math.min.apply(Math, numbers)
3-3:验证是否是数组(前提是toString()方法没有被重写过)
var obj = [1,2,3,4]
Object.prototype.toString.call(obj)
3-4:类(伪)数组使用数组方法
Array.prototype.slice.call(document.getlementByTagName('div'))
4.面试题
4-1:定义一个 log 方法,让它可以代理 console.log 方法
function log() {
console.log.apply(console, arguments)
}
4-2:接下来的要求是给每一个 log 消息添加一个"(app)"的前辍,比如:
function log() {
var args = Array.prototype.slice.call(arguments)
args.unshift('1------>')
console.log.appyl(console, args)
bind
var altwrite = document.write;
altwrite("hello"); // 会报错,因为altwrite() 改变了this指向,指给了window
修改如下:
altwrite.bind(document)('hello') // 利用bind改变this指向到docment
或者
altwrite.call(document, 'hello')
题目:
this.num = 9;
var mymodule = {
num: 81,
getNum: function() {
console.log(this.num);
}
};
mymodule.getNum(); // 81
var getNum = mymodule.getNum;
getNum(); // 9, 因为在这个例子中,"this"指向全局对象
var boundGetNum = getNum.bind(mymodule);
boundGetNum(); // 81
四.高阶函数
1.概念:英文叫Higher-order function。JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
2.例如map、reduce、filter
2-1:map()作为高阶函数,事实上它把运算规则抽象了
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']
2-2:reduce
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
return x + y;
});
2-3:filter
var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {
return x % 2 !== 0;
});
五.变量提升
1. 函数提升在变量提升上面
console.log(fn)
var fn = 10
console.log(fn)
function fn() {
console.log(10)
}
console.log(fn)
依次输出结果是:
function() {console.log(10)} ---- 10 ---- 10
六. 同步任务和异步任务的优先顺序
1. js在一个时间只能干一件事 -- 单线程
2. js遇见异步任务先放入异步队列中,等同步任务执行完成再去执行异步任务
七. 异步任务的放入时间和执行时间
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i) // 3 3 3
},10)
}
1. 输出四个3的理由
1-1:js 执行的时候会先执行同步任务,setTimeout是异步任务
1-2:根据js运行机制,setTimeout 10毫秒之后会放到异步任务中
1-3:10毫秒for早就执行完了,最终的i已经是3了
2. 作用域解决 var 改为 let
2-1:let为块级作用域, 变量 i 只存活到每次for循环大括号结束,var的i是不收for循环限制的
3. 闭包解决
3-1:这个是通过自执行函数返回一个函数,然后在调用返回的函数去获取自执行函数内部的变量,此为闭包
3-2:闭包会导致内存占用过高,因为变量都没有释放内存
for (var i = 0; i < 4; i++) {
setTimeout(
(function(i) {
return function() {
console.log(i);
};
})(i),
300
);
}
八.promise 的发展史
1. jq-deferred
1-1:在1.5版本之前的ajax,所有的成功都在success方法里面
1-2:在1.5版本之后的ajax,就可以存在多个.done .file 在到后来的.then就和promise非常像了
九. 堆和栈的区别
1. 栈:
1-1:栈内存一般储存基础数据类型;按值访问;存储的值大小固定;由系统自动分配内存空间;空间小,运行效率高;先进后出,后进先出;栈中的DOM,ajax,setTimeout会依次进入到队列中,当栈中代码执行完毕后,再将队列中的事件放到执行栈中依次执行。
2. 堆:
2-1:堆内存一般储存引用数据类型;按引用访问;存储的值大小不定,可动态调整;主要用来存放对象;空间大,但是运行效率相对较低
例如:
var a1 = 0; // 栈
var a2 = 'this is string'; // 栈
var a3 = null; // 栈
var b = { m: 20 }; // 变量b存在于栈中,{m: 20} 作为对象存在于堆内存中
var c = [1, 2, 3]; // 变量c存在于栈中,[1, 2, 3] 作为对象存在于堆内存中
[图解](https://juejin.im/post/5b1deac06fb9a01e643e2a95)
十. es6笔试题
var obj = [
{ id:3, parent:2 },
{ id:1, parent:null },
{ id:2, parent:1 },
]
转换为:
var obj = {
id: 1,
parent: null,
child: {
id: 2,
parent: 1,
child: {
id: 3,
parent: 2
}
}
}
前者转换后者
var newarr = obj.sort((a, b) => b.parent - a.parent).reduce((acc, cur) => {
return acc ? { ...cur, child: acc } : cur
}, {})
后者转前者
fn(o) {
const { child, ...other } = o
return child ? [other, ...this.fn(child)] : [other]
}