最近看到有人分享16届前端进阿里(前端学习群的群主),17届前端杭州收割offer(优秀的路人)。 恰好同为17届的我最近也很轻松拿了2个offer,可是我看到他们写的题目回答并非很详细,因此强迫症的我也想来和你们分享下,我会把全部没挂掉的和挂掉的都分享出来,遇到的每一道题目都作详细的分析,因此文章有点长哦。javascript
长文慎入哈,可是有酒有故事,小板凳我都给你搬好啦,快坐下!css
故事开始咯~html
原本想等过完年回来再换工做的,可是前段时间有猎头在简书私信我,说看个人文章写得还行,问我想不想换工做。 而后我就更新了简历,发过去了,邀请我面试,可是这家公司在北京 /西安,后来就没去,可是故事就此开始了,这反而促使我走上了换工做的道路。前端
贴一下自认为写得不错的几篇文章:vue
CSS3动画卡顿性能优化解决方案html5
深度剖析0.1 +0.2===0.30000000000000004的缘由java
如何解决0.1 +0.2===0.30000000000000004类问题node
记一次简单的DOM XSS攻击实验python
你真的理解==和===的区别吗?react
本身从大三暑假实习到如今工做半年多,接近快1.5+年的时间,一门心思扎到前端开发领域,高强度式的工做和学习,买书啃书,写代码看代码,写博客看博客,提问题回答问题,投入了几乎是大一到大三学习时间总和的时间学习前端开发。
最近阴差阳错开始笔试,面试,虽然都没怎么准备,可是很轻松就收获了一家阿里子公司,一家浙大系公司的offer,再加上昨天被第三家面试官的赞扬,以前的妄自菲薄一下烟消云散。
因为本身此次换工做,但愿可以在一家公司呆至少2年以上,很是但愿能和一帮对前端技术有看法有思考有追求的小伙伴们,一块儿去探索钻研有趣的前端世界。因此我决定尝试更多的面试,加入到一个更适合本身的团队。
同时也但愿经过这篇面试分享,可以给和我同样努力的前端小伙伴们一些鼓励,也但愿可以把最真实的本身展示给你们。
拒绝了第一家主动找到个人公司后,又有家阿里子创业公司在 BOSS 直聘里私信我,而后抱着测试水平的心态面了。 现场笔试不怎么样,可是他们看到我简书文章还算能够,就让我重作了一遍题目: 一些特别棒的面试题[0], 最后一道单词与字符串匹配的题的拓展,都是面试官一步步引导我去深刻的,感受学习到不少。
没想到结果是比较让人惊喜的,前辈说个人学习能力比较强,因此愿意给我这个机会,给的薪资比如今高 2K,关键是据说有许多项目能够作,听说面试个人老板之前也是在阿里的技术专家。
贴一下面试题和个人回答。
1.说一下你熟悉的设计模式
2.说一下你理解的模块机制
3.MVVM原理
4.最熟悉的框架路由机制
5.状态管理
6.统计字符串中单词出现次数
复制代码
我最熟悉的设计模式:工厂模式(ES5),组件设计模式(ES6) 工厂模式(ES5,基于prototype。此例中基类Base,子类Factory)
var Factory = function () {
if(!(this instanceof Factory)){
return new Factory();
}
}
Factory.prototype = Object.assign(new Base(), {
version: '0.0.1',
defaultOption:{
title:'标题'
},
init:function (cfg) {
this.title = cfg.title || '';
this.currentOption = Object.assign(this.defaultOption,{
//...
})
},
render: function () {
var option = this.currentOption;
this.chart.setOption(option);
},
showTitle: function () {
this._showTitle();
}
})
复制代码
组件设计模式(ES6,基于class,方便继承和初始化,也是React组件的推荐写法,我比较喜欢。此例中父类Compnent,子类Retrive)
class Retrive extends Component {
constructor (props) {
super(props);
this.state = {
name:''
};
this.getRemoteData = this.getRemoteData.bind(this);
}
getRemoteData (data) {
this.state.retriveResult = data;
}
render(){
return (
<div className="Retrive">
<Button name="search" onClick={this.getRemoteData}>查询</Button>
</div>
);
}
}
复制代码
AMD: 异步模块加载规范。 a.js,定义一个依赖jQuery和echrts的组件。
define(['jquery', 'echarts'], function ($, echarts) {
var AMD = function(){}
AMD.prototype = {
title:'',
foo: function(){}//AMD类或者继承AMD类的子类的属性
}
function bar(){}//返回,公共属性
function baz(){} //未返回,私有属性
return {
main:AMD,
bar: bar
}
});
复制代码
若是b.js依赖a.js,能够这样
define(['./a'], function (a) {
//调用构造函数,foo
var instance_amd = new a.main();
instance_amd.foo()
//调用bar
a.bar()
});
复制代码
ES6 modules: 和python的包机制很相似,导入import,导出export。
1.场景:vue,react推荐机制,须要babel转义成es5以兼容浏览器。
2.关于import...(from...)
①.import...from...的from命令后面能够跟不少路径格式,若只给出vue,axios这样的包名,则会自动到node_modules中加载;若给出相对路径及文件前缀,则到指定位置寻找。
②.能够加载各类各样的文件:.js、.vue、.less等等。
③.能够省略掉from直接引入。
3.关于export
①.导出的能够是对象,表达式,函数,类
②.导出是为了让别人导入
4.言外话:使用es6的话,有一个特别好的规范去遵照,airbnb的es6规范(https://github.com/airbnb/javascript)
复制代码
CommonJS:nodejs中使用较多,关键词是require,没写过node包,只引用过别人的模块,因此内部实现原理不是很清楚。
MVVM是一种软件架构模式,MVVM有助于先后端分离。 View:视图层,粗略理解为DOM。 Model:与数据库对应的model,通常为json格式,做为req的body经过http(s)与数据库实现通讯。 ViewModel:View与Model经过ViewModel实现双向绑定。
核心是提供对View和ViewModel的双向数据绑定,这样使得ViewModel的改变View当即变化,MVVM在前端的实现有:angular,vue,react。
vue中的经常使用数据双向绑定。
view:{{message}}
viewModel v-model="message"
model:message
复制代码
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})
复制代码
单文件组件中的话,就多了一个用html5的template标签将view和viewModel包裹起来,model部分停留在script标签部分。
<template>
view
viewModel
</tamplate>
<script>
model
</script>
<styles>
为了让view好看点
</styles>
复制代码
react的话,我在使用的过程当中,没有据说过双向绑定的东西,对redux reducers推荐写为纯函数印象深入,纯函数的话,感受应该有点单项数据流的意思。
既然说到框架了,说一个最让我感受有趣的点,那就是组件间的通讯,对于简单组件,只涉及父子级别的通讯的,vue使用on emit的方式,react使用props。对于复杂级别通讯,爷爷父亲儿子孙子等等时,vue推荐使用vuex,react推荐使用redux,统一的全局状态树用来作状态管理很是好,可使得逻辑很是清晰。vue项目文件结构研究不深,react的项目文件结构的话,presentational和containers的设计方法感受很是有道理,一个负责视图一个负责数据,很是清爽。
vue路由依赖:vue-router 经过组合组件来组成单页应用程序,只须要将组件映射到路由便可。 前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求。 须要注意2种模式的区别:hash模式和history模式,hash模式会在后面加一个很丑的#,能够开启history去掉。 hash模式原理:它的特色在于:hash 虽然出如今 URL 中,但不会被包括在 HTTP 请求中,对后端彻底没有影响,所以改变 hash 不会从新加载页面。hash能够理解为锚点,例如./index.html/#/foo,hash值为#/foo,这样不会跳转页面。就至关于统一页面的不一样锚点,页面间跳转与 ./index.html/#foo到./index.html/#bar相似。
./store/index.js
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/common',
name: 'common',
component: Common
}
]
复制代码
路由层面还会包括嵌套路由,动态路由以及重定向,至关于本身模仿浏览器请求而后服务器响应模式,其实不涉及向后端请求,仅在浏览器就能够实现页面跳转,前段时间我作的用户权限控制就用到了vue-router,相比MVC结构下的后端路由,清晰了很多,这样后端只要负责路由编写api就好。
下面是我在用vuex作项目时的一些思考,简单修改了一下,也添加了一些关于redux的思考。
vuex state,前端data view,前端DOM actions,用户操做,引发data变化从而致使DOM变化。
多个组件(视图)共享状态:通俗来说,就是多个组件间会通讯时,致使从后端拿来的数据发生变化,当组件较多时,若是兄弟组件间的通讯都依赖父组件进行通讯,会致使组件间的耦合很是高,从而致使项目逻辑混乱,难以维护。
多个组件(视图)依赖于同一状态。 来自不一样视图的行为须要变动同一状态。
全局单例模式管理,把组件的共享状态抽取出来 无论在组件树的哪一个位置,任何组件都能获取状态或者触发行为!
实践出真知:
1.state存放在index.js中,建立的Store实例getter,mutations,actions等,能够分离出来
2.getters存放在getter.js中,数据流为state→getter→组件,getter至关于一个数据获取过滤器,从仓库拿特定数据到组件,至关于对computed的集中处理。
3.mutations存放在mutations.js中,数据流为组件→mutations→state,mutations至关于一个数据提交发射器,从组件提交数据到仓库
4.actions存放在actions.js中,数据流为组件→actions→mutations→state,异步操做的主要场所。
5.modules是开发大型应用时须要用到的,每一个module都有单独的states,getters,actions以及mutation,有一股nodejs模块的味道。
复制代码
vuex三原则:
1.惟一数据源
2.保持状态只读
3.数据改变只能经过纯函数完成 更改 Vuex 的 store 中的状态的惟一方法是提交 mutation。
复制代码
必定要注意mutation和action的区别!
mutation只变动本地的状态,也就是说,直接只去修改store中的数据。 action包含异步操做,直接调用api,经过api的数据,再提交mutation。
能够说,action只比mutation多了一个异步调用api的操做,由于调用api后,通常有2种返回结果,succes或者error,或者是promise的多种状态,根据不一样的。
最近在学习redux,组件dispatch一个action到store,至关于发送一个http请求,而后store作出响应,返回一个response给组件。和vuex大体相似,惟一有区别的是,vuex还须要引入react-redux,引入Provider和connect链接组件和store。
" hi how are you i am fine thank you youtube am am ",统计"you"出现的次数。
function wordCount(str,word){
var str = str || "";
var word = word || "";
var strArr = str.split(" ");
var count = 0;
for(var i=0;i<strArr.length;i++){
if(word===strArr[i]){
count++
}
}
return count;
}
wordCount("hi how are you i am fine thank you youtube am am","you");
复制代码
若是字符串没有空格怎么办?
function wordCount(str,word){
var str = str || "";
var word = word || "";
var count = 0;
var index = str.indexOf(word);
while(index!==-1){
count++;
str = str.substr(index+word.length);
index = str.indexOf(word)
}
return count;
}
wordCount("hihowareyouiamfinethankyouyoutubeamam","you");
复制代码
若是不用js内置字符串函数,本身用每一个字符对比呢?
function wordCount(str,word){
var num = 0;
var str = str+" " || "";
var word = word || "";
var strArr = str.split("");
var wordArr = word.split("");
var count = 0;
function compare(arr1,a,arr2,b){
if(b+a<arr2.length){
if(arr1[a]===arr2[b+a]){
num++;
return compare(arr1,a+1,arr2,b+1)
}
if(num===arr1.length){
count++
num = 0;
}
}
}
for(var i=0;i<strArr.length;i++){
for(var j=0;j<wordArr.length;j++){
if(wordArr[wordArr.length-1]===strArr[i+wordArr.length-1]){
compare(wordArr,0,strArr,i+0)
}
}
}
return count;
}
wordCount("hihowareyouiamfinethankyouyoutubeamam","am");
复制代码
能够更加高效一些吗?
function wordCount (str,word) {
var str = str+" " || "";
var word = word || "";
var strArr = str.split("");
var wordArr = word.split("");
var wordArrLen = wordArr.length;
var count = 0;
var num = 0;
function compare (arr1,a,arr2,b) {
if(b+a<arr2.length){
if(arr1[a]===arr2[b+a]){
num++;
return compare(arr1,a+1,arr2,b+1)
}
if(num===arr1.length){
count++;
num = 0;
}
}
}
var j = 0;
while(j<wordArrLen){
var i = 0;
while(i<strArr.length){
if(wordArr[wordArrLen -1]===strArr[i+wordArrLen -1]){
compare(wordArr,0,strArr,i+0);
}
i++;
}
j++;
}
return count;
}
wordCount("hihowareyouiamfinethankyouyoutubeamam","a");
//1.调整最高层级遍历数组,从37的2次方降到3的2次方,从1369降到9
//2.合并控制变量和控制条件,使用while替代for,去除JS引擎查询i,j是否存在的消耗,会稍微下降代码可读性
//3.对重复引用的wordArr.length,赋值给局部变量wordArrLen,在这里,Array.prototype.length的查询次数从3次下降到1次
复制代码
我原本就不想再继续找了,可是今天朋友给我一个建议,跳槽前多投几家,说不定能够找到更好的,而后我就想到 V 站上看到好多求职贴,因此就索性再试试看有没有更好的坑。
简历: 前端开发-简书-趁你还年轻233
简书 : 趁你还年轻233
github: FrankKai
segmentFault:趁你还年轻
坐标:杭州
而后我在V站上发了贴,颇有缘分,一个V友加了个人好友,约好时间进行了一次电话面试,后面了解好公司技术氛围和商议好薪资待遇后,成功拿到了offer,薪资待遇和第一家差很少,这家是浙大系的公司。
我和面试官很聊得来,最后甚至都聊到了吃鸡,因为电话面试太愉快了,因此此次电话面试到如今我仅记得一道跨域题了。第三家公司面试官也很赞,天气寒冷,友情提醒我注意路上冰冻,不过因为第三家面试在明天晚上才进行,因此我把第三家的BOSS直聘题也总结在一块儿了:一些特别棒的面试题[1]
贴一下面试题和个人回答。
1.平时有遇到过跨域问题吗?
2.下面这段代码最终输出什么?
3.["1","2","3"].map(parseInt)返回的是什么?
4.下面代码中“入库”的颜色是?
复制代码
说到跨域问题,就必定要说到同源,什么是同源,相同协议,相同域名,相同端口,即为同源。
不一样源之间的通讯就会有跨域问题,通常来讲是客户端访问服务器,服务器上去配置跨域。 我遇到的跨域问题都是后端去配置一下就能够解决的,好比我前端在用vue的官方推荐异步请求库axios,去请求后端的koa开启的后端服务时,就会遇到跨域的问题,例如koa使用依赖koa-cors就能够,具体的话,就是Access-Control-Allow-Origin: 源名,能够为*或者是特殊的源。或者是传统的maven或者nginx上,也能够很方便的配置跨域。
JSONP有用过吗?JSONP没用过,可是原理貌似是经过js加载一个script DOM标签进来,而后在新的script的src中引入想要执行的代码。 其实跨域问题在后端中也有相似的,只不过是叫作进程间通讯,有IPC,RPC等等方式进行进程间通讯。
let O = function(name){
this.name = name || 'world';
};
O.prototype.hello = function(){
return function(){
console.log('hello ' + this.name);
};
};
let o = new O;
let hello = o.hello();
hello();
复制代码
年轻的个人答案是:hello world。
答案显然是不对的,由于这是一道陷阱题,陷阱就在于O.prototype.hello调用后,return的是一个函数,这么作的话,在执行新实例o的hello方法是,this其实已经变成了window。
那么答案是hello undefined吗?
年轻的你又错了,并非。
而是 hello 。
请注意,是hello ,而不是hello undefined,而是空字符串
缘由就在于window.name是事先是有定义的,并且其值为空。
不信的话你能够在控制台打印window.name,返回的是"",你再打印window.yourname试试看,好比window.frank,返回的就是undefined了。
感谢@ygh1的提醒,打印结果和运行环境也是有关的,由于node中全局是global,browser中全局是window。
刚在node里跑了下有陷阱的题目,打印出来确实是hello undefined,由于node中的global对象没有初始的name属性。
因此最正确的答案应该是:
node环境:hello undefined
browser环境:hello _____(非零宽空字符)
复制代码
而我在工做中,遇到的更多的常见的像上面同样的工厂函数式的写法是这样的。
let O = function(name){
this.name = name || 'world';
};
O.prototype.hello = function(){
console.log('hello ' + this.name);
};
let o = new O("frank");
let hello = o.hello("frank");
复制代码
打印结果为:hello frank。
若是不传入frank的话,打印出的是默认值hello world。
A. [1,2,3] B.["1","2","3"] C.[1,1,1] D.其余
这特么又是一道陷阱题,还好我以前在看MDN的map函数时,看到过这个陷阱。
正确答案是D:其余。
其余的具体值为多少?[1,NaN,NaN]。
不敢相信吧,为何不是可爱的[1,2,3]呢?
由于map的callback有3个参数,currentValue,index和array,parseInt有2个参数,string和radix(进制),只传入parseInt到map中的话,会自动忽略第三个参数array,可是不会忽略index,因此就会把0,1,2做为第二个参数传给parseInt。
若是还不明白的话,咱们把["1","2","3"].map(parseInt)的每一步都拆开来。
parseInt("1",0) 此时将字符"1"转换为O进制数,因为0进制数不存在,因此返回Number类型的1。
parseInt("2",1) 此时将字符"2"转换为1进制数,因为超出进制数1,因此返回NaN。
parseInt("3",2) 此时将字符"3"转换为2进制数,因为超出进制数2,因此返回NaN。
复制代码
至此,真相大白。 那么经常使用的非陷阱式map写法是怎样的呢?
像这样:["1","2","3"].map(x=>parseInt(x))
传一个完整的函数进去,有形参,有callback,这样就不会形成由于参数传入错误而形成结果错误了,最后返回一个漂漂亮亮的经由纯函数处理后的新数组回来。
其实这里若是再深刻的话,能够再考察纯函数是什么?
纯函数其实就是一个不改变输入,可是能够借助输入,产生一个以输入为原材料,通过加工处理后,输出一个全新的输出的函数,关键在于不改变输入,纯函数是编写redux的reducer必须具有的技能点。
刚才公司的大牛过来,说他历来不用parseInt,他用加号,+"1" 返回1,+"2"返回2。大牛果真大牛,黑科技是真的多。
<ul class="list" id="list">
<li class="favorite">
<span>出库</span>
</li>
<li class="favorite">
<span class="highlight">入库</span>
</li>
</ul>
<style>
#list .favorite:not(#list) .highlight{
color: red;
}
#list .highlight:nth-of-type(1):nth-last-of-type(1){
color: blue;
}
</style>
复制代码
A. red B.blue C.black
个人答案是:我猜一下,多是A,由于A的权重是最大的,伪类选择器的权值应该比较小吧。
面试官发来一个👍,明天能够来公司面谈吗?已经约好明天面试。
这道题的解答到此为止,由于我是真的真的对CSS不感兴趣,各位看官老爷请原谅我。
em rem
两者的为了保证用户修改字体大小时,保持垂直方向上的字体大小一致。与px不一样,两者都是字体计算大小单位,也就是说,须要经过计算得出其大小,转换成px,微信小程序的rpx也是这样,最后仍是转换成了px,多是借鉴了rem的思想吧。
可是em相对于继承来的父元素,rem相对于根元素。听大牛说,rem在国内使用比较多,可能侍使用习惯问题。我本身也以为rem使用起来更简单,为根元素的font-size赋一个初始值,再配合css的媒体查询,能够动态的改变这个全局的单位,能够说是牵一发而动全身,使用起来很是方便,而em的可阅读性就不好了,有的时候为了算字体大小,须要一级一级找上去,很是不直观。
现代的经常使用的浏览器,1rem等于16px,可是能够经过html{font-size:percentage/num }
来控制。
举2个 rem和em例子对比下。
html简写结构:
<html>
<body>
<div></div>
</body>
</html>
复制代码
rem 例子:
html { font-size:62.5%; } /* =10px */
body { font-size: 2.0rem; } /* =20px */
div { font-size: 1.0rem; } /* =10px */
复制代码
em 例子:
html { font-size:62.5%; } /* =10px */
body { font-size: 2.0em; } /* =20px */
div { font-size: 1.0em; } /* =20px */
复制代码
MDN的font-size章节给出了em和rem的很是好的解释,英文原版很是直观,我这里再贴一下:
em
Represents the calculated font-size of the element. If used on the font-size property itself, it represents the inherited font-size of the element.
rem
Represents the font-size of the root element (typically ). When used within the root element font-size, it represents its initial value (a common browser default is 16px, but user-defined preferences may modify this).
其实em和rem与MVVM框架的组件间通讯有些相似,都有逐级继承和全局影响的概念。em是逐级传递的,也就是继承,框架中用props和事件订阅发布的方式也是这样,爷,父,孙的传递都是要一级一级去传递的,爷爷想直接传授点技能给孙子必须先传授给父亲,爷爷→父亲→孙子;而rem就和框架中的状态管理库很像,例如vuex和redux,抽取出一个全局的状态树,不用一级一级的去很复杂的去继承,爷爷想教教孙子,直接就能够传授给孙子,爷爷→孙子。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<title>仅用一个DIV实现红绿灯</title>
<style>
@keyframes light{
0%{
background-color: green;
left: 0;
}
33.3%{
background-color: green;
left: 0;
}
33.4%{
background-color: yellow;
left: 200px;
}
66.6%{
background-color: yellow;
left: 200px;
}
66.7%{
background-color: red;
left: 400px;
}
99.9%{
background-color: red;
left: 400px;
}
}
.traffic-light{
position: relative;
width: 200px;
height: 200px;
border-radius: 50%;
animation: light 3s ease-in-out 0s infinite;
}
.container{
width: 600px;
border:10px solid #000;
border-radius: 20% 20%;
}
</style>
</head>
<body>
<div class="container">
<div class="traffic-light"></div>
</div>
</body>
</html>
复制代码
因为种种缘由,开始了新一轮的面试,这一轮面试可谓收获颇丰。 与各类各样的面试官交流下来,除了收获到一些疏漏的知识点外,发现面试其实很是考验面试官的水平。揣摩出一些如何成为一名合格的前端面试官方法。
以及很重要的老哥的经验:进大厂前必需要作的准备,提早一个月刷题。
console.log(1);
setTimeout(function(){
console.log(2);
},0);
console.log(3);
复制代码
输出:1 3 2 缘由:Call Stack会最后调用setTimeout的callback,setTimeout中的callback是一个异步函数。 函数调用栈的部分能够参考这里:blog.risingstack.com/node-js-at-…
console.log(typeof foo);
var foo = "foo";
function foo(){}
复制代码
输出:function
console.log(typeof foo);
function foo(){}
var foo = "foo";
复制代码
输出:function
function优先级比var高,不管在其先后,都会覆盖掉同名的var声明。
for(let i=0;i<6;i++){
setTimeout(function(){
console.log(i)
},0)
}
console.log(i)
复制代码
输出:
0
Uncaught ReferenceError: i is not defined
1
2
3
4
5
复制代码
为何在debug的过程当中,打印顺序是混乱的? (等把规范的timers章节翻译完,再来解决) breakpoint打在console.log(i)上。
Uncaught ReferenceError: i is not defined
0
2
5
4
3
1
复制代码
若是将let替换成var呢?
for(var i=0;i<6;i++){
setTimeout(function(){
console.log(i)
},0)
}
console.log(i)
复制代码
输出: 6个6 缘由:
若想回答这个问题,须要深刻理解Object.prototype.toString.call()。
难道真的像本身理解的那样,是经过call将[1,2,3]做为Object.toString的实参传递了进去吗?不是。 直接Object.toString([1,2,3])不能实现一样的功能吗?不能。 而实际上也有Array.proto.toString()这种形式,因此是能够直接调用arr.toString()的,这样能检测出吗?不行。
那究竟是什么缘由? 先来肝一个表格。
数据类型 | 例子 | return |
---|---|---|
字符串 | "foo".toString() | "foo" |
数字 | 1.toString() | Uncaught SyntaxError: Invalid or unexpected token |
布尔值 | false.toString() | "false" |
undefined | undefined.toString() | Uncaught TypeError: Cannot read property 'toString' of undefined |
null | null.toString() | Uncaught TypeError: Cannot read property 'toString' of null |
String | String.toString() | "function String() { [native code] }" |
Number | Number.toString() | "function Number() { [native code] }" |
Boolean | Boolean.toString() | "function Boolean() { [native code] }" |
Array | Array.toString() | "function Array() { [native code] }" |
Function | Function.toString() | "function Function() { [native code] }" |
Date | Date.toString() | "function Date() { [native code] }" |
RegExp | RegExp.toString() | "function RegExp() { [native code] }" |
Error | Error.toString() | "function Error() { [native code] }" |
Promise | Promise.toString() | "function Promise() { [native code] }" |
Obejct | Object.toString() | "function Object() { [native code] }" |
Math | Math.toString() | "[object Math]" |
为何会出现下面的状况?
Object.toString.call(Array)//"function Array() { [native code] }"
Object.prototype.toString.call(Array)//"[object Function]"
复制代码
答案在这里!
Object.toString()//"function Object() { [native code] }"
Object.prototype.toString()//"[object Object]"
复制代码
Object对象和它的原型链上各自有一个toString()方法,第一个返回的是一个函数,第二个返回的是值类型。
既然知道了不一样,如今咱们再来分析下Object.prototype.toString.call(Array)//"[object Function]"
。 Array对象自己返回一个构造函数,Array//ƒ Array() { [native code] },而Object.prototype.toString()返回的是//"[object Type]"的形式,经过call将Array的this上下文切换到Object,从而调用了Object.prototype.toString(),所以返回[object Function]
。
须要注意的是:Math.toString()直接返回"[object Math]"。
实际开发中,咱们用到最多的多是:Object.prototype.toString.call([1,2,3])//"[object Array]"这种。
总结:
那么不能够直接Array.prototype.toString.call([1,3,4])吗? 不行! 由于Array,Function,Date虽然是基于Object进行建立的,可是他们继承的是Object.toString(),而不是Object.prototype.toString()。 再加深一遍印象:
Object.toString()//"function Object() { [native code] }"
Object.prototype.toString()//"[object Object]"
复制代码
因此这就是必须用Object.prototype.toString()去检测类型的缘由。
至于Object.prototype.toString()内部是怎么实现的,等到时机成熟再去深刻。
var obj = {
a: 1,
name: 'world',
objSayName: function (fn) {
fn();
}
}
function sayName () {
return console.log(this.name);
}
obj.objSayName(sayName);
// 输出:undefined
复制代码
为何? 在obj的objSayName内部,没有修改this指向到当前调用对象。
题目一:对象内部方法,调用全局的函数 适用:多个对象(局部方法)复用同一全局函数 精简:局部(方法)复用全局函数 方法:修改this指向,经过Function.prototype.bind()去显式修改this指向到当前调用对象。 缘由:Calling f.bind(someObject) creates a new function with the same body and scope as f, but where this occurs in the original function,in the new function it is permanently bound to the first argument of bind, regardless of how the function is being used.bind only works once!
var obj = {
name: '1',
objSayName: function (f) {
var g = f.bind(this);
console.log(g());
}
};
function sayName(){
return this.name;
}
obj.objSayName(sayName);
复制代码
输出:'1'
拓展: 题目二:若是全局方法想输出对象的局部属性,该怎么办? 适用:同一全局函数输出多个对象(内部变量) 精简:全局函数输出局部(变量) 方法:使用apply或者call修改this指向到被调用对象 缘由:An object can be passed as the first argument to call or apply and this will be bound to it.
var obj = {
name: '1',
say: function (fn) {
fn();
}
};
function sayName(){
return this.name;
}
sayName.apply(obj);
sayName.call(obj);
// 输出:'1'
复制代码
参考:developer.mozilla.org/en-US/docs/…
现东家的面试题,是我最欣赏的一套面试题,在这里已经工做了接近1年时间,成长许多。
题目主要分为4个部分:
答案未验证准确性,有待验证并更新。 我将按照“1 year before”和“today”的角度去进行解答,也是对本身成长的一个记录。
1 year before: cookies,须要与后端协做,一种认证方式,expires失效时间 sessionStorage,当前会话有效 localStorage,本地缓存,expires失效时间 today:
<script>
、<script async>
和<script defer>
区别1 year before: 常见的同步;异步;延迟加载。
today:
<script async>
控制浏览器同异步加载脚本。若设置async为false,那么浏览器在HTML解析期间同步加载脚本。通常来讲经过代码document.creatElement()
插入脚本是异步的,设置async为false能够将其控制为同步。<script defer>
defer属性用来控制script是否在HTML完成解析以后再执行,可是会在DOMContentLoad
燃烧以前完成。拓展: 1.什么是DOMContentLoaded
事件? DOMContentLoaded事件,会在初始化HTML document完成加载和解析触发,无需等待stylesheets,images,以及字frame结束加载。Load
事件和DOMContentLoaded
事件很相似,常常有人混淆2个事件。 2.什么是Load
事件? load事件,会在完成一次彻底加载以后再触发。 3.如何解决同步js阻塞DOM渲染的问题,最快速度渲染DOM? 异步化js,优化stylesheets加载。 4.如何检查document是否完成加载? document.readyState
。
因此一次加载文档资源的状态有3个,loading``interactive
和complete
。
<head></head>
之间,而将js<script></script>
放置在</body>
以前?1 year before: 防止阻塞浏览器渲染,先执行CSSOM,再绘制DOM,再操做DOM。主线程单线程渲染UI。
today 从技术实现的角度讲,先加载stylesheets而且绘制,DOM渲染结束后再加载并执行可能涉及DOM操做的js文件,避免js的执行致使渲染阻塞。 从用户体验的角度讲,优先加载样式表减小用户面对空白网页的时间,提高用户体验。
1 year before ①ID优先级高 ②ID惟一标识,不能重复
today ①语法不一样,#idname, .classname ②影响元素不一样,#idname做用单个元素,.classname做用全部元素 ③选择器权重不一样,#idname权值高于.classname
1 year before
today [译]如何隐藏DOM元素?
*{ box-sizing: border-box }
的做用,而且说明使用它有什么好处?1 year before 怪异盒模型。 计算方便,width包含了border,响应式百分比布局。
today box-sizing的值有2种,一个是content-box,一个是border-box,content-box仅仅包含content。 border-box的width会将content, padding和border包含在内,例如width:100%指的是包含了content,padding和border的宽度,布局时更好控制。
例如子元素继承了父元素的width:100%,此时设置了子元素padding,border,若子元素的box-sizing是content-box,会致使溢出,而border-box的话,width:100%会很舒服地包含了padding和border。
由于这样的应用场景不少,因此索性就为全部标签都设置成border-box,有特殊状况的再手动设置成content-box。
有一篇墙推IE怪异盒模型的文章:把全部元素的box-sizing都设置成border-box吧!
1 year before
<div style=""></div>
每一个选择器都有权值,权重为和,#foo>.class::before 三者和为权重,权值id>标签>class>伪类。
today 优先级由高到低
<span style=""></span>
一个很优秀的说明css选择器优先级的图:specifishity.com/
通用选择器(*),组合符(+,>,〜,'',||)和否认伪类(:not())对权重没有影响。
1 year before absolute
减小了计算,translate()不影响其余元素,性能更好,GPU计算次数更少
today 在个人这篇博问中有答案:CSS3动画卡顿性能优化解决方案
translate()涉及到的是合成器线程,与主线程是相互独立的,因此会比较快。 而absolute positioning涉及到的是主线程,会致使主线程负责的布局重绘和js执行,因此会比较慢。
1 year before
today 关于这个问题曾经产出2篇博客: 从规范去看Function.prototype.apply究竟是怎么工做的? 从规范去看Function.prototype.call究竟是怎么工做的? call是索取,apply是付出。 从call和apply的字面意思就能够看出,call调用,apply应用,调用函数,应用参数。 call和apply的主要区别在于,call仅仅切换this上下文到其余类,从而调用本身不存在的方法;而apply主要是为了将其余类型的参数传递到本身内部,再调用本身的方法。
假设有foo,bar。 foo.call(bar)
bar调用foo的方法,实例Object.toString([1,2,3])->"[object Array]",数组实例调用了Object类的toString方法。 foo.apply(null, bar)
foo的方法应用bar的参数,Math.max.apply(null, [1,2,3])->3,Math的max方法应用了[1,2,3]中的每一个参数。
1 year before JSONP 原理:异步插入一个<script></script>
,会有XSS问题 缘由:没有调用XMR对象 today 一种比较古老的不安全的跨域请求访问方式,没有调用XMR对象,服务器容许浏览器在query parameter中传递浏览器内定义的函数,而这个函数是有几率被XSS攻击改写的。
来自StackOverflow高票答案:stackoverflow.com/questions/2…
JSONP不是一个很复杂的问题。 假设咱们在example.com
域名下,此时咱们想给example.net
域名发送一个请求。为了请求成功,须要跨越域名边界,这对于浏览器来讲是禁忌。
绕开这个限制的一种方式是<script>
标签。当你使用script标签的时候,域名限制会被忽略,可是对结果不能作任何处理,脚本是被评估过的。
开始进入JSONP。当你发送一个请求到支持JSONP的服务器,你会传一些特殊的关于你的页面的数据给服务器。这种方式下,服务器会以你的浏览器处理起来方便的方式包装响应。
例如,服务器须要一个叫作callback的参数去开启JSONP功能。而后你的请求会想下面这样: http://www.example.net/sample.aspx?callback=mycallback
没有JSONP的状况下,这能够返回一些基本的JS对象,例如: {foo: 'bar'}
然而,在支持JSONP的状况下,服务器接收到callback参数,它包裹结果的方式是不同的,会返回下面这样的数据: mycallback({foo: 'bar'});
就如你缩减,他会调用浏览器端的方法。因此,在你的页面上callback的定义以下:
mycallback = function(data){
alert(data.foo);
};
复制代码
如今的话,在脚本加载成功后,它会被评估,而后函数执行。cross-domain请求成功!
因此JSONP存在一个很严重的问题:你失去了对请求的控制。例如,没法得知错误代码的返回。可使用定时器去监控请求,可是这不是很好。JSONRequest是一个很是好的跨域脚本执行的方式,安全,而且得到更多对请求的控制。
2015年,CORS是一个与JSONRequest能够抗衡的方案。JSONP在老式浏览器下仍旧有用,可是不安全。 CORS是作跨域请求访问的更安全、更高效的一种方式。
1 year before jade vue angular react { ... }
或者{{ ... }}
today: 一些好用的模板引擎库
经常使用的仍是前端框架自带的以及pug,因为我只对vue.js的比较熟悉因此就没有罗列react和angular的demo。
1 year before 这个问题写过博客,看过规范。 ①===是==的子集 ②==有类型转换 ③规范内实现机制不一样 today 你真的理解==和===的区别吗?
1 year before 浏览器安全机制
today 如何理解same-origin policy?
1 year before axios
today
1 year before 解决了callback hell问题。
today 以一个图片资源加载绘制canvas。
class CanvasImage {
constructor(url, width, height) {
this.width = width || 500;
this.height = height || 500;
this.url = url || '';
this.element = new Image(this.width, this.height);
}
get imageElement() {
return this.element;
}
}
复制代码
callback hell方式
source.onload = function() {
const target = new CanvasImage(url, 80, 80).imageElement;
target.onload = function() {
const main = new MainCanvas(source, target, 'right').canvasElement;
context.drawImage(main, 0, 0, 500, 500);
};
};
复制代码
Promise方式
const sourcePromise = new Promise((resolve) => {
setTimeout(() => {
const target = new CanvasImage(url, 80, 80).imageElement;
resolve(target);
}, 0);
});
source.onload = function() {
sourcePromise.then((target) => {
const main = new MainCanvas(source, target, 'right').canvasElement;
context.drawImage(main, 0, 0, 500, 500);
});
};
复制代码
async/await方式
function sourceCanvasImage() {
return new Promise((resolve) => {
setTimeout(() => {
const target = new CanvasImage(url, 80, 80).imageElement;
resolve(target);
}, 0);
});
}
async function mergeCanvas() {
const targetElement = await sourceCanvasImage();
const main = new MainCanvas(source, targetElement, 'right').canvasElement;
context.drawImage(main, 0, 0, 500, 500);
}
source.onload = function() {
mergeCanvas();
};
复制代码
1 year before
today 官方的解释更加权威:nodejs.org/de/docs/gui…
什么是Event Loop?event loop容许Node.js执行非阻塞的I/O操做-----尽管JS是单线程的-----它尽量地经过把操做卸载到系统内核(kernel)。
由于大多数现代的内核(kernel)是多线程的,它们能够在后台处理多个操做。当其中之一完成后,kernel会告诉Node.js一个适当的callback能够被添加到poll queue(轮询队列)中而且执行。
更多原理上的内容,能够参考:[译]Node.js Event Loop,Timers和 process.nextTick()
event loop,原理细节包括如下内容
其中前三部分属于main thread,能够阅读node源码一探究竟。最后的background thread属于libuv的部分,能够去深刻libuv源码(这是一个专门处理异步的c语言库)理解其实现机制。
可是阅读源码须要很是好的基础。这里推荐一篇囊括了以上知识点的很是走心的文章:blog.risingstack.com/node-js-at-…
var foo = 10 + '20';
1 year before: 30
today: "1020"
add(2, 5);// 7
add(2)(5);// 7
复制代码
1 year before 经过arguments类数组对象。
function foo(){
var sum = 0;
for(var i = 0; i<arguments.length; i++){
sum += arguments[i]
}
if(arguments.length>=2){
return sum;
}else{
return foo;
}
}
复制代码
add(2)(5)运行失败。
today
const add = (a, b) => {
const result =
a && b
? a + b
: function(b) {
return a + b;
};
return result;
};
复制代码
var foo = "Hello";
(function() {
var bar = " world";
alert(foo + bar);
})();
alert(foo + bar);
复制代码
1 year before "undefined World" "Hello undefined"
today "Hello world" // 当即执行函数做用域能够访问全局变量 Uncaught ReferenceError: bar is not defined // 全局做用域不能够访问局部变量
console.log('one');
setTimeout(function() {
console.log('two');
}, 0);
console.log('three');
复制代码
1 year before one three two
today one three two
缘由:优先执行函数调用栈中任务,setTimeout排入宏任务队列延后执行。 event loop包括call stack,(宏)任务队列,微任务队列和background thread,而call stack中的普通代码优先执行,setTimeout会经由background thread进入宏任务队列,宏任务队列间隙执行微任务队列。
function getResult(value, time){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log(value);
resolve();
}, time);
});
}
(async () => {
const a = getResult(1, 300);
const b = getResult(2, 200);
await a;
await b;
})();
(async () => {
await getResult(1, 300);
await getResult(2, 200);
})();
复制代码
1 year before 1 2
today 2 1 1 2
这是一道考察async的题目:如何理解async函数?
var obj = {
a: 1,
name: 'world',
objSayName: function(fn) {
fn();
}
}
var name = 'hello';
var arr = [1, 2, 3, 4, 5];
function foo(o) {
var bar = arr || [6, 7, 8];
var arr = [4, 2, 9];
var baz = o;
baz.a = 2;
console.log(bar, obj.a);
}
function sayName() {
return console.log(this.name);
}
foo(obj);
obj.objSayName(sayName);
复制代码
1 year later [4,2,9] 2 'hello'
today [6,7,8] 2 'hello'
缘由: 变量提高。
var bar,arr,baz;
bar = arr || [6, 7, 8];// 此时arr是undefined,所以bar获得了[6, 7, 8]赋值
arr = [4, 2, 9];
复制代码
baz得到了obj的引用,全部修改baz至关于修改obj,因此打印出2.
由于闭包,全局的sayName函数内的this指向全局,因此是hello。
我想成为一个很厉害的人,让这个世界由于有了我而有一点点不同,哪怕,只有一点点
贴一下本身理想中的团队的关注点
1.我的前端技术的成长空间
好比资深前端对初级前端的指导,前端团队成员间的学习和提高等等 这个最重要,个人目标是2年时间成为基础扎实,有独立前端技术看法的开发者
2.公司对员工成长和进步的重视程度
例如新人培训,技术分享,技术翻译等等
3.公司加班状况是996仍是10105或者是按照项目进度安排的
只要是一帮追求前端极致的人,996是彻底OK的
4.员工补贴,福利
例如过节补贴,工会福利
5.绩效考核和奖金
例如绩效考核,年终奖金
此次年后换工做我但愿本身能在一家公司呆至少2年以上,因此这些方面我都须要了解清楚再作决定,须要慎重一些,但愿可以加入一个有趣而且有实力的团队~
简历戳这里: 前端开发-简书-趁你还年轻233
最后再说句体题外话,3家公司都在用vue,都问我有没有用过vue...
That's it !
趁你还年轻
2018年1月31日
于冻成狗的冰雪杭州
如何成为一名合格的面试官?