type
Post
status
Published
date
Sep 18, 2017
slug
summary
tags
文字
category
碎片杂文
icon
password
饿了么
1.箭头函数和普通函数的区别
箭头函数中的 this 和调用时的上下文无关;
箭头函数在定义之后,this 就不会发生改变了,无论用什么样的方式调用它,this 都不会改变; 但严格来说,这并不是“取决于定义时的上下文”, 因为箭头函数根本就没有绑定自己的 this,在箭头函数中调用 this 时,仅仅是简单的沿着作用域链向上寻找,找到最近的一个 this 拿来使用罢了; 从效果上看,这和我的理解并没有多大偏差,但它们的本质却是截然不同,箭头函数并不是普通函数新增了 this 不受调用时上下文影响的特性,而是减少了很多特性; 实际上箭头函数中并不只是 this 和普通函数有所不同,箭头函数中没有任何像 this 这样自动绑定的局部变量,包括:this,arguments,super(ES6),new.target(ES6)…… 所以我个人认为箭头函数更适合函数式编程,除了它更短以外,使用箭头函数也更难被那些没有显示声明的变量影响,导致你产生意料之外的计算结果; 如果是像当初的我一样简单的考虑固定住 this 这个易变的家伙……那倒是很简单,有些常用的方法,比如这样:
function make() { var self = this; return function() { console.log(self); }; }
或者
function make() { return function() { console.log(this); }.bind(this); }
然而第二种方法只能固定 this 这一个变量而已,如前文所述,箭头函数中的 arguments 等变量也是从作用域链中寻找的,为了实现类似的效果,我们只有重新定义一个局部变量这一种方式,而 babel 也是使用这种方式对箭头函数进行处理的。
function make() { return () => { console.log(this); console.log(arguments); }; } //babel it... function make() { var _this = this, _arguments = arguments; return function() { console.log(_this); console.log(_arguments); }; }
2.箭头函数可以用作构造函数吗?
不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。 使用箭头函数时需要注意:
(1)函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
(3)不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 Rest 参数代替。
(4)不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
this 指向的固定化,并不是因为箭头函数内部有绑定 this 的机制,实际原因是箭头函数根本没有自己的 this,导致内部的 this 就是外层代码块的 this。正是因为它没有 this,所以也就不能用作构造函数。 除了 this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target。
3.js 中有哪些改变函数作用域的方法?
call(),apply(),bind() 例如,有一个函数 func1 定义如下:
var func1 = function(arg1, arg2) {};
就可以通过 func1.call(this, arg1, arg2); 或者 func1.apply(this, [arg1, arg2]); 来调用。其中 this 是你想指定的上下文,他可以任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里 是如果我们有一个对象 whiteDog = {food:“bone”},我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 blackCat 的 say 方法:blackCat.say.call(whiteDog);所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法,但是其他的有,我们可以借助 call 或 apply 用其它对象的方法来操作。
bind:
obj.bind(thisObj, arg1, arg2, ...);
把 obj 绑定到 thisObj,这时候 thisObj 具备了 obj 的属性和方法。与 call 和 apply 不同的是,bind 绑定后不会立即执行。
同样是 add()和 sub():
add.bind(sub, 5, 3); //不再返回 8 add.bind(sub, 5, 3)(); //8 如果 bind 的第一个参数是 null 或者 undefined,等于将 this 绑定到全局对象。
4.bind()返回的是什么,写一个 bind 的 ployfill
bind()返回了一个函数,《你不知道的 javascript》93 页
5.vue 父子组件通信
vue 双向数据绑定 vue 组件通信、数组值的修改
6.position 有哪几种,分别相对谁定位,哪些脱离了文档流?
position 的值为 absolute、fixed 的元素脱离文档流,static、relative 没有脱离文档流
客如云
1.谈一谈 react 的原理和它的整个生命周期是什么样的?每个生命周期可以做些什么?
2.react 比 jQuery 好在哪?
3.浏览器的卡顿通常原因是什么?react 性能优化有哪些方法?
4.javascript 的错误类型有哪些?
Error 对象有两个属性:message,name
ECMA-262 定义了下列 7 种错误类型:
Error; EvalError(eval错误); RangeError(范围错误); ReferenceError(引用错误); SyntaxError(语法错误); TypeError(类型错误); URIError(URI错误);
var a = new Array(-1) // Uncaught RangeError: Invalid array length console.log(b) // Uncaught ReferenceError: b is not defined var 1c // Uncaught SyntaxError: Invalid or unexpected token var d = new 10 // Uncaught TypeError: 10 is not a constructor decodeURI('%2') // Uncaught URIError: URI malformed
URI 错误: encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和 unescape()
另外还有 try…catch 相关
5.谈一谈 javascript 这门语言和其他语言的区别?
6.javascript 的 function 有哪些作用?
1.关键字,声明一个函数时使用
2.创建一个函数作用域
涉及概念:立即执行函数,闭包
3.创建一个对象
4.匿名函数
7.translateX 和 position 调整位置有什么区别?
position 会重新渲染文档
100 课堂
1.http、https 和 websocket 区别?
2.前后端分离后的协作方式?get 请求和 post 请求区别?
3.git 你常用哪些命令?前端项目打包上线你是怎么做的?
4.对 linux 命令行有多少了解?要建立一个 a->b->c 这样一级级的目录怎么创建?
5.canvas 在什么时候会开启 GPU 渲染?
6.你现在的项目中有用到哪些 html5 标签?它们有什么含义?
7.css poisition、float 一系列相关
CSS 优先级
position:absolute; 生成绝对定位的元素,相对于 static 定位以外的第一个有定位的父元素进行定位。
z-index 相关
清除浮动:
.clearfix { *zoom: 1; //兼容IE, 加*是hack写法,只有IE6、7识别 &:after{ clear:both;// 项目中还额外增加了一些属性 } }
8.javascript 相关问题
1)true or false
[]==[] // false []===[] // false NaN==NaN // false NaN===NaN // false
===
宽松相等,==
严格相等。首先要注意并不是===
做的工作更多,其实是==
在发现左右两边类型不同时,先进行强制类型转换再比较。1、2 是由于[]数组是对象,对象的
==
比较会比较对象的引用,即比较两个对象是否是同一个对象的引用 3、4 很特殊,因为 NaN 的特殊,它不等于自身!2)如何判断是否为数组?如何判断数组为空?如何判断数组相等?如何判断对象相等?
3)数组有哪些方法?
4)闭包
for (var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); }, i * 1000); }
这是闭包的经典问题,这个问题及其答案的完整叙述如下:
写一段代码,第一秒输出 1,第二秒输出 2,以此类推,到 5。
新手会写出下面的代码
for (var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); }, 1000); }
或者稍好点的这种
for (var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); }, i * 1000); } // 每秒1个6
上面的代码之所以不能达到我们的效果,涉及到的是 javascript 的作用域这个知识点。上面每个延迟函数的回调(setTimeout 中的 timer)其实都只在一个共享的全局作用域中,因此也就共享了一个 i 的引用。本质上等价于:不使用循环,将延迟函数中的回调重复定义 5 次。
所以解决的思路是:5 次的回调,每次都用一个自己的作用域,并在这个作用域中有一个 i 的副本。这样理解之后或许会出现下面的代码,利用 IIFE(立即执行函数)来创建函数作用域
for (var i = 1; i <= 5; i++) { (function() { setTimeout(function timer() { console.log(i); }, i * 1000); })(); } // 每秒1个6,用立即执行函数来创建一个函数作用域,创建了作用域,但是是空的(没有i的副本)
于是稍作修改:
for (var i = 1; i <= 5; i++) { (function() { var j = i; setTimeout(function timer() { console.log(j); }, j * 1000); })(); }
改进上面的方法:
for (var i = 1; i <= 5; i++) { (function(j) { setTimeout(function timer() { console.log(j); }, j * 1000); })(i); }
其实我们本质上需要的是每次循环都能创建一个新的作用域,它并不一定非要是函数作用域,我们不使用 IIFE,而使用块级作用域,let 就可以声明一个被限制在块级中的变量
for (var i = 1; i <= 5; i++) { let j = i; setTimeout(function timer() { console.log(j); }, j * 1000); }
还有更酷的做法:
for (let i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); }, i * 1000); }
上面的代码看上去理所应当,不过正确的解释是: for 循环头部的 let 声明有一个特殊行为。这个行为指出变量在循环中不止被声明一次,每次迭代都会声明,随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。
其实还有个解法:
for (var i = 1; i <= 5; i++) { setTimeout( function timer(j) { console.log(j); }, i * 1000, i ); }
这是由于 setTimeout 其实还可以接收参数传给回调,而每个参数 j 其实是不同作用域中的
5)setTimeout
console.log(1); setTimeout(function() { console.log(2); }, 0); console.log(3); // 1, 3, 2 console.log(1); setTimeout(function() { console.log(2); }); console.log(3); // 1, 3, 2
不传参的话,浏览器会默认一个最小执行时间,Firefox 是 100ms,其他浏览器一般是 10ms,都比 0 大。所以,主要解释第一段代码的输出结果,大都数开发者或许就能说出两个字“异步”。
而这个题主要考察的是对 setTimenout 的理解,进而引申出对 javascript 单线程的思考:
JavaScript 引擎是基于事件驱动单线程执行的,JavaScript 引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个 JavaScript 线程在运行 JavaScript 程序。
setTimeout(…,0)的作用其实是做了一个异步调度,可以理解为“把这个函数插入到当前事件循环的队列结尾处,0 秒后执行”。
其实这个理解还需要一点补充:延迟并不会被设置成 0 秒,setTimeout 有一个最小执行时间,正如之前提到的;也并不是立即被放到队尾,而是程序自己找一个合适的时机,因此若有两个 setTimeout(…,0)并不知道谁先执行。
- 作者:Wave52
- 链接:https://vercel.wuchengran.com/article/288644c7-1637-4702-a421-26a4fe4f62a4
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章