2019-12-03面试题部分整理

一道关于原型链的面试题
function C1(name){
    if(name) this.name=name;
}
function C2(name){
    this.name=name;
}
function C3(name){
    this.name=name||'john';
}
//
C1.prototype.name="Tom";
C2.prototype.name="Tom";
C3.prototype.name="Tom";
alert((new C1().name)+(new C2().name)+(new C3().name));

//我理解的本地name都没声明,访问的都是prototype的name属性
//所以有个疑问就是没有声明的属性也可以去原型里找吗
//不是应该声明没赋值才有用么

分析:
  C1,if不成立,new C1()中没有name属性,就访问到了原型上的name,输出tom
  C2,既然没有参数,也就是执行new C2(undefined),所以name为undefined
  C3,new C3()的name值为john,所输出john
详解JS函数柯里化

https://www.jianshu.com/p/2975c25e4d71

一步步看清 async/await 和 promise 的执行顺序

https://juejin.im/post/5dc28ea66fb9a04a881d1ac0

https://segmentfault.com/a/1190000017224799

splice和slice、map和forEach、 filter()、reduce()的区别

1.slice(start,end):方法可以从已有数组中返回选定的元素,返回一个新数组,
包含从start到end(不包含该元素)的数组方法
注意:该方法不会更新原数组,而是返回一个子数组
2.splice():该方法想或者从数组中添加或删除项目,返回被删除的项目。(该方法会改变原数组)
splice(index, howmany,item1,...itemx)
·index参数:必须,整数规定添加或删除的位置,使用负数,从数组尾部规定位置
·howmany参数:必须,要删除的数量,
·item1..itemx:可选,向数组添加新项目
3.map():会返回一个全新的数组。使用于改变数据值的时候。会分配内存存储空间数组并返回,forEach()不会返回数据
4.forEach(): 不会返回任何有价值的东西,并且不打算改变数据,单纯的只是想用数据做一些事情,他允许callback更改原始数组的元素
5.reduce(): 方法接收一个函数作为累加器,数组中的每一个值(从左到右)开始缩减,最终计算一个值,不会改变原数组的值
6.filter(): 方法创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。它里面通过function去做处理

['1', '2', '3'].map(parseInt) 和 ['1', '2', '3'].fliter(parseInt)的输出结果是什么?

eg:
['1', '2', '3'].map(parseInt)
// [1, NaN, NaN]

其实在使用map时,map的callback的第二个参数index引索值就成为parseeInt的radix值。['1', '2', '3'].map(parseInt)在遍历的过程。其实是经历了下面的过程。
parseInt('1', 0);
parseInt('2', 1);
parseInt('3', 2);

parseInt('1', 0):radix的值为0,判断字符串发现介于1~9,用10进制转换,结果为1.
parseInt('2', 1):radix的值为1,如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。
parseInt('3', 2): radix的值为2,这就意味着字符串将被解析成字节数,也就是仅仅包含数值0和1。parseInt的规范指出,它仅尝试分析第一个字符的左侧。这个字符串的第一个字符是“3”,它并不是基础基数2的一个有效数字。所以这个子字符串将被解析为空。如果子字符串被解析成空了,函数将返回为NaN。


['1', '2', '3'].map(parseFloat)
// [1, 2, 3]

parseFloat相对于parseInt比较简单,不用考虑第二个参数,只需要看第一个参数是否能正常转换为数字就行。
parseFloat('1');  // 1
parseFloat('2');  // 2
parseFloat('3');  // 3

一个小的知识点:如何快速将一个字符串数组转化为数字类型的数组
['1', '2', '3'].map(parseFloat)
['1', '2', '3'].map(Number)



['1', '2', '3'].filter(parseInt)
// ["1"]

filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或等价于 true 的值的元素创建一个新数组。
parseInt('1', 0);
parseInt('2', 1);
parseInt('3', 2);

parseInt('1', 0):radix的值为0,判断字符串发现介于1~9,用10进制转换,结果为1,所以callback的结果等价于true,返回元素'1'。
parseInt('2', 1):radix的值为1,如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN,结果不等价于true,不返回。
parseInt('3', 2): radix的值为2,这就意味着字符串将被解析成字节数,也就是仅仅包含数值0和1。parseInt的规范指出,它仅尝试分析第一个字符的左侧。这个字符串的第一个字符是“3”,它并不是基础基数2的一个有效数字。所以这个子字符串将被解析为空。如果子字符串被解析成空了,函数将返回为NaN。
2、['1', '2', '3'].filter(parseFloat)
['1', '2', '3'].filter(parseFloat)
// ["1", "2", "3"]

使用parseFloat时,遍历之后结果的每一项都是结果等价于true,所以全部返回。

CSS3中transition和animation的属性分别有哪些
盒模型
forEach,map和filter的区别
实现函数柯里化
跨标签页的通讯方式有哪些
(1) BroadCast Channel
(2) Service Worker
(3) LocalStorage + window.onstorage监听
(4) Shared Worker + 定时器轮询(setInterval)
(5) IndexedDB + 定时器轮询(setInterval)
(6) cookie + 定时器轮询(setInterval)
(7) window.open + window.postMessage
(8) Websocket
javascript中函数声明与函数表达式的区别

1、以函数声明的方法定义的函数,函数可以在函数声明之前调用,而函数表达式的函数只能在声明之后调用。
2、以函数声明的方法定义的函数,函数名是必须的,而函数表达式的函数名是可选的。
3、在Javscript中,解析器在向执行环境中加载数据时,对函数声明和函数表达式并非是一视同仁的,解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问),至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。
4、Javascript 中函数声明和函数表达式是存在区别的,函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用。这个微小的区别,可能会导致JS代码出现意想不到的bug,让你陷入莫名的陷阱中
5、函数声明的优先级高于变量,如果变量名跟函数名相同且未赋值,则函数声明会覆盖变量声明;如果函数有多个同名参数,那么最后一个参数(即使没有定义)会覆盖前面的同名参数;
6、附:每个程序都要做的工作,程序开始先预解析语法,标点符号是否有误,解析内存是否可容纳,解析变量。。。直到解析无误了,程序才开始正常的流程顺序走。试想一下,如果没有预解析顺序,直接按照流程顺序走,额能程序执行到最后一个函数,发现了语法错误,才开始报错,那性能要有多差啊!

例子1:
fn()
var fn = function() {
    console.log("这是函数表达式")
}
// fn is not a function

fn()
function fn() {
    console.log("这是函数声明")
}
// 这是函数声明

例子2:
变量的查找是就近原则去寻找,定义的var变量;
第二点,变量的声明被提前到作用域顶部,赋值保留在原地,如下demo;
var a=10;
function aaa(){
  alert(a);
  var a=20;
}
aaa(); //结果为:undefined

例子3:
  ar a=10;
  function aaa(a){ 
      alert(a);
      var a=20;  //因为 a 是形参,优先级高于 var a; 所以 局部变量a的声明其实被忽略了。
  } 
  aaa(a); //结果为:10

例子4:
var a=10; 
function aaa(){ 
    alert(a);
};            
function bbb(){
var a=20;
aaa();
}
bbb(); //结果为10,
因为aaa()函数不能访问到bbb()里面的局部变量,所以访问到的是a=10,这个全局变量。
此外,bbb()中的aaa()只是调用外部的函数,这一点要注意!如果放在里面,结果不一样

例子5:
var a=10; 
function test(){ 
    alert(a);  // 函数执行后,在函数环境内,没有找到本层环境关于a的声明,所以开始向上一层环境查找。
    a = 20;  // 执行到这行开始改变全局a的量
};    
test();  // 10
alert(a); // 20 。  全局环境的a在函数执行时已经被改变


出现上面的错误是因为用函数声明创建的函数可以在函数解析后调用(解析时进行等逻辑处理);而用函数表达式创建的函数是在运行时进行赋值,且要等到表达式赋值完成后才能调用。其本质原因体现在这两种类型在Javascript function hoisting(函数提升)和运行时机(解析时/运行时)上的差异。

如何实现浏览器内多个标签页之间的通信? (阿里)

调用localstorge、cookies等本地存储方式

如何判断当前脚本运行在浏览器还是node环境中?(阿里)

通过判断Global对象是否为window,如果不为window,当前脚本没有运行在浏览器中

精确判断对象的类型

JavaScript 中一切都是对象,任何都不例外,对所有值类型应用Object.prototype.toString.call() 方法结果如下

console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]
window load 和document ready的区别

document ready 实质是DOMContentLoaded事件
所有的节点渲染完毕,就执行
load事件要等到所有的资源加载完毕,如图片,视频,js脚本等

实现对数组进行乱序

分析:这道题检验了数组的 sort() 方法,因为是乱序,所以还需要用到 Math.random() 产生随机数,打乱排序规律!

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var sign = 1; 
a.sort(function() {
    return Math.random() - 0.5
});
请写出异步加载js方案,不少于两种

(1) defer,只支持IE
(2) async:
(3) 创建script,插入到DOM中,加载完毕后callBack

defer延迟执行,并行加载js文件,会按照页面上script标签的顺序执行
async异步加载,并行加载js文件,下载完成立即执行,不会按照页面上script
标签的顺序执行
增进:
• JavaScript代码的加载和执行会阻塞下面HTML的渲染
• 二者都是异步去加载外部JS文件
• Async是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行;而
Defer是在JS加载完成后,整个文档解析完成后执行。
• Defer更像是将<script>标签放在</body>之后的效果,但是它由于是异步加
载JS文件,所以可以节省时间。

递归函数


function factorical(num){
  if(num <= 1){
    return 1;
  } else{
    return num * factorical(num - 1);
  }
}
factorial(2)//2

介绍JS有哪些内置对象?

数据封装类对象:Object、Array、Boolean、Number、String
其他对象:Function、Arguments、Math、Date、RegExp、Error
ES6新增对象:Symbol、Map、Set、Promises、Proxy、Reflect

实现一个函数clone,可以对JavaScript中的5种主要的数据类型(包括Number、String、Object、Array、Boolean)进行值复制。
统计字符串中字母个数以及统计最多字母数。
输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和和最大值。

例如输入的数组为1,-2,3,10,-4,7,2,-5,和最大的子数组为3,10,-4,7,2,因此输出该子数组的和1。

如果需要手动写动画,你认为最小时间间隔是多久?

16.7ms 多数显示器默认频率是60Hz,即1秒刷新60次,所以理论上最小间隔: 1s / 60 * 1000 = 16.7ms

split() join() 的区别

答案:前者是将字符串切割成数组的形式,后者是将数组转换成字符串
例子

var a=new Array();
    a[0]="XHTML";
    a[1]="CSS";
    a[2]="JavaScript";
    alert(a.join("#")); //XHTML#css#JavaScript
alert(a.join()); //XHTMLcssJavaScript

split(a,b)方法:用于把一个字符串分割成字符串数组. a是必须的,决定个从a这里开始分割
b不是必须的,可选。该参数可指定返回的数组的最大长度 。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。
注意返回的数组中不包括a本身;

 var str="how are you?";
 console.log(str.split(""));//h,o,w, ,a,r,e, ,y,o,u,?
 console.log(str.split("a"));//how ,re you? 不包含a本身
 console.log(str.split(" "));//how,are,you?
 console.log(str.split("",3));//h,o,w
 ["h", "o", "w", " ", "a", "r", "e", " ", "y", "o", "u", "?"]
 ["how ", "re you?"]
 ["how", "are", "you?"]
 ["h", "o", "w"]
javascript的typeof返回哪些数据类型. 六种

答案:string,boolean,number,undefined,function,object

例举3种强制类型转换和2种隐式类型转换?

强制(parseInt,parseFloat,number)
隐式(== ===)

Javascript的事件流模型都有什么?

“事件冒泡”:事件开始由最具体的元素接受,然后逐级向上传播

“事件捕捉”:事件由最不具体的节点先接收,然后逐级向下,一直到最具体的

“DOM事件流”:三个阶段:事件捕捉,目标阶段,事件冒泡

  1. 如何阻止事件冒泡
    答案:ie:阻止冒泡ev.cancelBubble = true;
    非IE ev.stopPropagation();

  2. 如何阻止默认事件
    答案:(1)return false;(2) ev.preventDefault();

介绍事件“捕获”和“冒泡”执行顺序和事件的执行次数?

按照W3C标准的事件:首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段
事件执行次数(DOM2-addEventListener):元素上绑定事件的个数
注意1:前提是事件被确实触发
注意2:事件绑定几次就算几个事件,即使类型和功能完全一样也不会“覆盖”
事件执行顺序:判断的关键是否目标元素
非目标元素:根据W3C的标准执行:捕获->目标元素->冒泡(不依据事件绑定顺序)
目标元素:依据事件绑定顺序:先绑定的事件先执行(不依据捕获冒泡标准)
最终顺序:父元素捕获->目标元素事件1->目标元素事件2->子元素捕获->子元素冒泡->父元素冒泡
注意:子元素事件执行前提 事件确实“落”到子元素布局区域上,而不是简单的具有嵌套关系

在一个DOM上同时绑定两个点击事件:一个用捕获,一个用冒泡。事件会执行几次,先执行冒泡还是捕获?

该DOM上的事件如果被触发,会执行两次(执行次数等于绑定次数)
如果该DOM是目标元素,则按事件绑定顺序执行,不区分冒泡/捕获
如果该DOM是处于事件流中的非目标元素,则先执行捕获,后执行冒泡

.javascript阻止事件冒泡和浏览器的默认行为:

功能:停止事件冒泡  
function stopBubble(e) {
    // 如果提供了事件对象,则这是一个非IE浏览器
    if ( e && e.stopPropagation ) {
        // 因此它支持W3C的stopPropagation()方法 
        e.stopPropagation();
    } else { 
        // 否则,我们需要使用IE的方式来取消事件冒泡
        window.event.cancelBubble = true;
    }
}

 功能:阻止事件默认行为
function stopDefault( e ) {
     // 阻止默认浏览器动作(W3C)
     if ( e && e.preventDefault ) {
         e.preventDefault();
     } else {
        // IE中阻止函数器默认动作的方式
        window.event.returnValue = false;
    }
    return false;
}

JS中避免命名冲突的三个方法(有些同学或许会想不到这些概念)

答:命名空间,闭包,匿名函数

flex 布局 与 grid 布局。

flex:
grid: https://www.jianshu.com/p/d183265a8dad

实现 Vue SSR 。
从 SPA 使用最小成本迁移到 SSR 。

实现方法: (未完成) 根据指定元素,在数组里面找出 ff 数组(ff 数组这个名字是我瞎说的)。比如数组 [2, 3, 6, 7] ,指定元素 7,则 ff 数组是 [2, 2, 3](2+2+3 = 7)和 [7]。若指定元素 6,则 ff 数组为 [2, 2, 2], [3, 3], 和 [6] 。

实现 Promise.finally。

finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作,使用方法如下:
Promise
.then(result => { ··· })
.catch(error => { ··· })
.finally(() => { ··· })

finally 特点:
不接收任何参数。finally 本质上是 then 方法的特例。
Promise.prototype.finally = function (callback) {
let P = this.constructor
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
)
}

另一种方式实现 Vue 的响应式原理。

Vue 的响应式原理是使用 Object.defineProperty 追踪依赖,当属性被访问或改变时通知变化。
有两个不足之处:
不能检测到增加或删除的属性。
数组方面的变动,如根据索引改变元素,以及直接改变数组长度时的变化,不能被检测到。
原因差不多,无非就是没有被 getter/setter 。
第一个比较容易理解,为什么数组长度不能被 getter/setter ?
在知乎上找了一个答案:如果你知道数组的长度,理论上是可以预先给所有的索引设置 getter/setter 的。但是一来很多场景下你不知道数组的长度,二来,如果是很大的数组,预先加 getter/setter 性能负担较大。
现在有一个替代的方案 Proxy,但这东西兼容性不好,迟早要上的。
Proxy,在目标对象之前架设一层拦截。具体,可以参考 http://es6.ruanyifeng.com/#docs/reference

Vue 组件 data 为什么必须是函数。

理解两点:
每个组件都是 Vue 的实例。
组件共享 data 属性,当 data 的值是同一个引用类型的值时,改变其中一个会影响其他。

Vue computed 实现。

从两个问题出发:
建立与其他属性(如:data、 Store)的联系;
属性改变后,通知计算属性重新计算。
实现时,主要如下
初始化 data, 使用 Object.defineProperty 把这些属性全部转为 getter/setter。
初始化 computed, 遍历 computed 里的每个属性,每个 computed 属性都是一个 watch 实例。每个属性提供的函数作为属性的 getter,使用 Object.defineProperty 转化。
Object.defineProperty getter 依赖收集。用于依赖发生变化时,触发属性重新计算。
若出现当前 computed 计算属性嵌套其他 computed 计算属性时,先进行其他的依赖收集。
参考:https://segmentfault.com/a/1190000010408657

diff 算法实现。
Vue complier 实现。

以前写过一篇 「Vue 生面周期总结的文章 」的文章,里面提到了 complier 的作用,没有做深入了解。。。
模板解析这种事,本质是将数据转化为一段 html ,最开始出现在后端,经过各种处理吐给前端。随着各种 mv* 的兴起,模板解析交由前端处理。 总的来说,Vue complier 是将 template 转化成一个 render 字符串。 可以简单理解成以下步骤:
parse 过程,将 template 利用正则转化成 AST 抽象语法树。
optimize 过程,标记静态节点,后 diff 过程跳过静态节点,提升性能。
generate 过程,生成 render 字符串。
参考:
https://segmentfault.com/a/1190000006990480
https://github.com/answershuto/learnVue/blob/master/docs/%E8%81%8A%E8%81%8AVue%E7%9A%84template%E7%BC%96%E8%AF%91.MarkDown

快排及其优化。

前端对算法的要求还是比较低的,但也是必不可少的一部分。
找到一篇比较不错的文章:https://www.cnblogs.com/zichi/p/4788953.html

缓存算法实现及其优化(缓存算法简单模型:假设可以缓存三个数据,请求前三个数据时,直接进缓存列表,当请求第四个数据时,若命中缓存,将被缓存的数据放入缓存列表头部,否则把新加入的数据放入缓存列表头部,淘汰最后一个数据)。

最简单的一种思路就是使用数组存储,然后让我优化。 我。。。一脸懵逼。 有兴趣的同学可以参考这个: http://www.cnblogs.com/dolphin0520/p/3749259.html
ps: 看来我得补补数据结构和算法相关的知识了。

怎么快速定位哪个组件出现性能问题。

当面试官问这个问题,没有 get 到面试官的点,扯了一堆乱七八糟没用的 - -。 后来面试官说主要是用 timeline 工具。 大意是通过 timeline 来查看每个函数的调用时常,定位出哪个函数的问题,从而能判断哪个组件出了问题。
附上两个使用 timeline 的文章:
https://juejin.im/post/5a6e78abf265da3e3f4cf085
https://developers.google.cn/web/tools/chrome-devtools/?hl=zh-cn

http 状态码 202, 204 。

面试官不知道为何扯到了 202, 204。。。好像是由自己带进坑的。- -
202: 服务器已接受请求,但尚未处理。 204: 服务器成功处理了请求,没有返回任何内容。
这些状态码感觉只要能记住常用的就 ok 了,当然还得了解 200 +, 300+, 400+, 500+ 代表什么意思。

WebSocket 。

WebSocket 应该算是一个比较常问的面试点,如果问的不深的话,应该比较好回答。
由于 http 存在一个明显的弊端(消息只能有客户端推送到服务器端,而服务器端不能主动推送到客户端),导致如果服务器如果有连续的变化,这时只能使用轮询,而轮询效率过低,并不适合。于是 WebSocket 被发明出来。
相比与 http 具有以下有点:
支持双向通信,实时性更强;
可以发送文本,也可以二进制文件;
协议标识符是 ws,加密后是 wss ;
较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头部;
支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)
无跨域问题。
实现比较简单,服务端库如 socket.io、ws ,可以很好的帮助我们入门。而客户端也只需要参照 api 实现即可。
参考:
http://www.ruanyifeng.com/blog/2017/05/websocket.html
https://www.cnblogs.com/chyingp/p/websocket-deep-in.html

null和undefined的区别?

null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
null表示"没有对象",即该处不应该有值。典型用法是:
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。

HTML语义化的理解?

(1)用正确的标签做正确的事情!
(2)html语义化就是让页面的内容结构化,便于对浏览器、搜索引擎解析;
(3)在没有样式CCS情况下也以一种文档格式显示,并且是容易阅读的。
(4)搜索引擎的爬虫依赖于标记来确定上下文和各个关键字的权重,利于 SEO。
(5)使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。

冒泡排序
移动端的点击事件的有延迟,时间是多久,为什么会有? 怎么解决这个延时?

1、300ms延迟由来
300 毫秒延迟的主要原因是解决双击缩放(double tap to zoom)。双击缩放,顾名思义,即用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。 那么这和 300 毫秒延迟有什么联系呢? 假定这么一个场景。用户在 iOS Safari 里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。 鉴于iPhone的成功,其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放,几乎现在所有的移动端浏览器都有这个功能。、

2、解决方案
(1)添加viewpoint meta标签
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
(2)FastClick
https://github.com/ftlabs/fastclick
移动端事件触发顺序:在移动端,手指点击一个元素,会经过:touchstart --> touchmove -> touchend -->click。
fastclick.js的原理是:FastClick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后真正的click事件阻止掉。
fastclick同样可以解决移动端点透现象。

怎么让Chrome支持小于12px 的文字?

Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决。
浏览器默认的margin和padding不同。解决方案是加一个全局的*{margin:0;padding:0;}这个方式现在用的比较少了,需要清晰知道每个元素的默认样式来统一。

写出不少于5种Math对象的方法,分别说明含义以及语法

Math.random()随机数0~1之间
Math.abs()绝对值
Math.floor() 向下取整
Math.ceil()向上取整
Math.min();
Math.max().....

用js实现类、继承(继承构造函数内部的属性方法及原型的属性和方法)、私有变量
function Car() {
    //构造函数
    this.name = "benz"; //属性
    var price = "1000000"; //私有变量,外部不能访问,只能通过下面的getPrice方法调用
    this.getPrice = function() {
        return price;
    }
}
Car.prototype.drive = function() {console.log("drive")}//给原型添加drive方法,创建出来的实例都有该方法
var c = new Car();//创建实例对象
javascript中判断一个对象中是否含有某一属性

1.使用in关键字。
该方法可以判断对象的自有属性和继承来的属性是否存在。
var o={x:1};
"x" in o; //true,自有属性存在
"y" in o; //false
"toString" in o; //true,是一个继承属性

2.使用对象的hasOwnProperty()方法。
该方法只能判断自有属性是否存在,对于继承属性会返回false。
var o={x:1};
o.hasOwnProperty("x");    //true,自有属性中有x
o.hasOwnProperty("y");    //false,自有属性中不存在y
o.hasOwnProperty("toString"); //false,这是一个继承属性,但不是自有属性

call() 和 apply() 、bind的区别?

我们先看一个简单的例子 1+1 = 2 你应该会吧


function add(a,b)  
{  
    alert(a+b);  
}  
function sub(a,b)  
{  
    alert(a-b);  
}  

add.call(sub,1,1);  //  结果是?  ‘2’  还是   ‘0’  呢

call的用法和意义:

call和apply可以用来重新定义函数的执行环境,也就是this的指向; call 和 apply 都是为了改变某个函数运行时的 context 即上下文而存在的 换句话说,就是为了改变函数体内部 this 的指向。因为 JavaScript 的函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念

call():语法:call(Obj, [arg1] [arg1])

大白话就是;

call 就是中间牵线的,sub说 我需要 add 你的方法 和技能,比如:add会飞天 ,但sub 不会飞,现在sub想飞,但add不让它飞,所以sub就叫来了 call 这个东西, call直接把add爆菊 加上一个 点 然后把sub抱到括号里,然后 sub 就直接把add 的“飞天”技能学会了,回到函数

function add(a,b)  
{  
    alert(a+b);  
}  
function sub(a,b)  
{  
    alert(a-b);  
}  

add.call(sub,1,1); 
 //  结果是 sub直接集成了 add 的“飞天”技能  sub alert里面 直接变成了 “a+b”!
//主角还是sub,并不是add ,你要搞清楚!

什么场景下会用到call, apply 这种装逼的写法呢;

function changeStyle(attr, value)
{     
    this.style[attr] = value; 
 }  
var box = document.getElementById('box');  
window.changeStyle.call(box, "height", "200px");

在这里,changeStyle函数将被box对象调用,this指向了box对象,如果不用call的话,程序报错,因为window对象中没有style属性。apply的用法:
window.changeStyle.apply(box, [‘height’, ‘200px’]);
现在 window对象 “box” 这个div 的高 直接变成200px了,
window.changeStyle.call(box, “height”, “200px”)
等价于 box.style.height=”200px”;
看到这里应该明白了吧 如果还没明白

apply() 这个用法
window.changeStyle.apply(box, [‘height’, ‘200px’]);
看了这个写法 你应该明白了把, 啊哈其实 就是写法 和形式不同而已,本质是一样的,apply(),是推进到数组里而已,也是为了改变this、这个,也是为了偷 add 的飞天技能哈!
总结一句话:call() 就是用来让括号里的对象 来集成括号外的函数的属性!可以称之为继承!

小结:

  1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
  2. 相同点:这两个方法的作用是一样的。
    都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。
    一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。

想要深入了解 call() 和 apply() 这两个方法,那么必须要先知道他们的基本作用:改变对象的执行上下文
什么是执行上下文?
我们在写一个方法的时候,总是会用到一个关键字this,而this的指向就是我们这里所说的执行上下文(执行环境)
首先我们要知道,this指向的永远是调用该方法的对象
为什么需要改变执行上下文?
简单来说,方便啊!复杂点说,原因可以有很多,得看具体的业务场景。下面还是举个例子 :小明有一个炒菜的铲子,小明的室友小刚今天突然想自己做菜吃,但是小刚没有铲子。小刚又不想为了做个菜单独买把铲子,于是就借用了小明的铲子,这样既达到了目的,又节省了开支,一举两得。
改变执行上下文也是一样,A对象有一个方法,而B对象因为某种不可言说的情况也需要用到一样的方法,那么这时候我们是单独为B扩展个方法呢,还是借用一下A的方法呢?当然是借用A的啦,既完成了需求,又减少了内存的占用

实现一个函数判断数据类型

function getType(obj) {
   if (obj === null) return String(obj);
   return typeof obj === 'object' 
   ? Object.prototype.toString.call(obj).replace('[object ', '').replace(']', '').toLowerCase()
   : typeof obj;
}

// 调用
getType(null); // -> null
getType(undefined); // -> undefined
getType({}); // -> object
getType([]); // -> array
getType(123); // -> number
getType(true); // -> boolean
getType('123'); // -> string
getType(/123/); // -> regexp
getType(new Date()); // -> date

有哪些值转换为布尔类型为FALSE

只有六个:undefined null 0 -0 NaN false 空字符串
增进: 任何对象转换为布尔类型都是true,比较坑的是空数组
快速将其他数据类型转换为布尔类型:!!

前端需要注意哪些SEO

1 合理的title、description、keywords:搜索对着三项的权重逐个减小,title值强调重点即可,重要关键词出现不要超过2次,而且要靠前,不同页面title要有所不同;description把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面description有所不同;keywords列举出重要关键词即可
2 语义化的HTML代码,符合W3C规范:语义化代码让搜索引擎容易理解网页
3 重要内容HTML代码放在最前:搜索引擎抓取HTML顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容一定会被抓取
4 重要内容不要用js输出:爬虫不会执行js获取内容
5 少用iframe:搜索引擎不会抓取iframe中的内容
6 非装饰性图片必须加alt
7 提高网站速度:网站速度是搜索引擎排序的一个重要指标

谈谈性能优化问题

代码层面:避免使用css表达式,避免使用高级选择器,通配选择器。
缓存利用:缓存Ajax,使用CDN,使用外部js和css文件以便缓存,添加Expires头,服务端配置Etag,减少DNS查找等
请求数量:合并样式和脚本,使用css图片精灵,初始首屏之外的图片资源按需加载,静态资源延迟加载。
请求带宽:压缩文件,开启GZIP,
代码层面的优化
用hash-table来优化查找
少用全局变量
用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能
用setTimeout来避免页面失去响应
缓存DOM节点查找的结果
避免使用CSS Expression
避免全局查询
避免使用with(with会创建自己的作用域,会增加作用域链长度)
多个变量声明合并
避免图片和iFrame等的空Src。空Src会重新加载当前页面,影响速度和效率
尽量避免写在HTML标签中写Style属性
移动端性能优化
尽量使用css3动画,开启硬件加速。
适当使用touch事件代替click事件。
避免使用css3渐变阴影效果。
可以用transform: translateZ(0)来开启硬件加速。
不滥用Float。Float在渲染时计算量比较大,尽量减少使用
不滥用Web字体。Web字体需要下载,解析,重绘当前页面,尽量减少使用。
合理使用requestAnimationFrame动画代替setTimeout
CSS中的属性(CSS3 transitions、CSS3 3D transforms、Opacity、Canvas、WebGL、Video)会触发GPU渲染,请合理使用。过渡使用会引发手机过耗电增加
PC端的在移动端同样适用

如何进行网站性能优化
  • content方面
  1. 减少HTTP请求:合并文件、CSS精灵、inline Image
  2. 减少DNS查询:DNS查询完成之前浏览器不能从这个主机下载任何任何文件。方法:DNS缓存、将资源分布到恰当数量的主机名,平衡并行下载和DNS查询
  3. 减少DOM元素数量
  4. 将资源放到不同的域下:浏览器同时从一个域下载资源的数目有限,增加域可以提高并行下载量
  5. 减少iframe数量
  6. 不要404
  • Server方面
  1. 使用CDN
  2. 添加Expires或者Cache-Control响应头
  3. 对组件使用Gzip压缩
  4. 配置ETag
  5. Flush Buffer Early
  6. Ajax使用GET进行请求
  7. 避免空src的img标签
  • Cookie方面
  1. 减小cookie大小
  2. 引入资源的域名不要包含cookie
  • css方面
  1. 将样式表放到页面顶部
  2. 不使用CSS表达式
  3. 使用不使用@import
  4. 不使用IE的Filter
  • Javascript方面
  1. 将脚本放到页面底部
  2. 将javascript和css从外部引入
  3. 压缩javascript和css
  4. 删除不需要的脚本
  5. 减少DOM访问
  6. 合理设计事件监听器
  • 图片方面
  1. 优化图片:根据实际颜色需要选择色深、压缩
  2. 优化css精灵
  3. 不要在HTML中拉伸图片
  4. 保证favicon.ico小并且可缓存
  • 移动方面
  1. 保证组件小于25k
  2. Pack Components into a Multipart Document

如何编写高性能的JavaScript?

遵循严格模式:"use strict";
将js脚本放在页面底部,加快渲染页面
将js脚本将脚本成组打包,减少请求
使用非阻塞方式下载js脚本
尽量使用局部变量来保存全局变量
尽量减少使用闭包
使用 window 对象属性方法时,省略 window
尽量减少对象成员嵌套
缓存 DOM 节点的访问
通过避免使用 eval() 和 Function() 构造器
给 setTimeout() 和 setInterval() 传递函数而不是字符串作为参数
尽量使用直接量创建对象和数组
最小化重绘(repaint)和回流(reflow)

前人建议

想知道自己什么水平就出去面试....
招人的要求越来越高,不要轻易离职,特别是裸辞
回答问题再棒,面试官(一般是你的直接领导面试),会考虑我要不要这个人做我的同事?所以态度很重要。(感觉像是相亲一样)

深拷贝、浅拷贝

参考:低门槛彻底理解JavaScript中的深拷贝和浅拷贝

https://juejin.im/post/5ad5b908f265da23870f540d
不错
附加:https://juejin.im/post/5dc25951e51d4561f81ad1d9

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

实现浅拷贝
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = obj1;
obj2.b = 100;
console.log(obj1);       // { a: 10, b: 100, c: 30 } <-- b 被改到了
console.log(obj2);  // { a: 10, b: 100, c: 30 }
或者
(1) Object.assign的方式 (2) 通过对象扩展运算符 (3) 通过数组的slice方法 (4) 通过数组的concat方法。


实现深拷贝
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;
console.log(obj1);  // { a: 10, b: 20, c: 30 } <-- b 沒被改到
console.log(obj2);  // { a: 10, b: 100, c: 30 }

深拷贝多种实现方式

  • 手动复制方式,如上面的代码
  • Object.assign,ES6 的新函数,可以帮助我们达成跟上面一样的功能。
var obj1 = { a: 10, b: 20, c: 30 };
 var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1); // { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2); // { a: 10, b: 100, c: 30 }
  • 转成 JSON 再转回来
    用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
    缺点:只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。
  • jquery,有提供一个$.extend可以用来做 Deep Copy。
    jQuery 的属性拷贝(extend)的实现原理是什么,如何实现深拷贝?
浅拷贝(只复制一份原始对象的引用)
 var newObject = $.extend({}, oldObject);
 
深拷贝(对原始对象属性所引用的对象进行进行递归拷贝) 
var newObject = $.extend(true, {}, oldObject);
  • lodash,也有提供_clonedeeep用来做Deep Copy;
  • 递归实现深拷贝
 function clone( o ) { 
        var temp = {}; 
        for( var k in o ) { 
            if( typeof o[ k ] == 'object' ){
                 temp[ k ] = clone( o[ k ] ); 
             } else {
                 temp[ k ] = o[ k ]; 
             } 
         } 
         return temp; 
     }

降维数组

判断一个字符串中出现次数最多的字符,统计这个次数

编写一个方法 求一个字符串的字节长度;

请用js计算1-10000中出现的0 的次数

请尽可能详尽的解释AJAX的工作原理。

http缓存知道哪些

http://blog.csdn.net/yzf91321...

讲一下事件循环机制

https://zhuanlan.zhihu.com/p/...

JavaScript 对象生命周期的理解?

当创建一个对象时,JavaScript 会自动为该对象分配适当的内存
垃圾回收器定期扫描对象,并计算引用了该对象的其他对象的数量
如果被引用数量为 0,或惟一引用是循环的,那么该对象的内存即可回收

描述浏览器的渲染过程,DOM树和渲染树的区别?

谈谈对前端安全的理解,有什么,怎么防范

前端安全问题主要有XSS、CSRF攻击
XSS:跨站脚本攻击
它允许用户将恶意代码植入到提供给其他用户使用的页面中,可以简单的理解为一种javascript代码注入。
XSS的防御措施:
过滤转义输入输出
避免使用eval、new Function等执行字符串的方法,除非确定字符串和用户输入无关
使用cookie的httpOnly属性,加上了这个属性的cookie字段,js是无法进行读写的
使用innerHTML、document.write的时候,如果数据是用户输入的,那么需要对象关键字符进行过滤与转义
CSRF:跨站请求伪造
其实就是网站中的一些提交行为,被黑客利用,在你访问黑客的网站的时候进行操作,会被操作到其他网站上
CSRF防御措施:
检测http referer是否是同域名
避免登录的session长时间存储在客户端中
关键请求使用验证码或者token机制
其他的一些攻击方法还有HTTP劫持、界面操作劫持

前端路由的原理

什么是路由?简单的说,路由是根据不同的 url 地址展示不同的内容或页面

早期的路由都是后端实现的,直接根据 url 来 reload 页面,页面变得越来越复杂服务器端压力变大,

使用场景?前端路由更多用在单页应用上, 也就是SPA, 因为单页应用, 基本上都是前后端分离的, 后端自然也就不会给前端提供路由。

前端路由主要有以下两种实现方案:

  • hash
  • history API

HTML5 History两个新增的API:history.pushState 和 history.replaceState,两个 API 都会操作浏览器的历史记录,而不会引起页面的刷新。

Hash就是url 中看到 # ,我们需要一个根据监听哈希变化触发的事件( hashchange) 事件。我们用 window.location 处理哈希的改变时不会重新渲染页面,而是当作新页面加到历史记录中,这样我们跳转页面就可以在 hashchange 事件中注册 ajax 从而改变页面内容。

优点

从性能和用户体验的层面来比较的话,后端路由每次访问一个新页面的时候都要向服务器发送请求,然后服务器再响应请求,这个过程肯定会有延迟。而前端路由在访问一个新页面的时候仅仅是变换了一下路径而已,没有了网络延迟,对于用户体验来说会有相当大的提升。

更多内容请看这里

前端路由的缺点

使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存。

1 基于hash

我们经常在 url 中看到 #,这个 # 有两种情况,一个是我们所谓的锚点,比如典型的回到顶部按钮原理、Github 上各个标题之间的跳转等,路由里的 # 不叫锚点,我们称之为 hash,大型框架的路由系统大多都是哈希实现的。

早期的前端路由的实现就是基于location.hash来实现的。其实现原理也很简单,location.hash的值就是URL中#后面的内容。比如下面这个网站,它的location.hash='#me':

https://www.srtian.com#me

此外,hash也存在下面几个特性:

  • URL中hash值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash部分不会被发送。
  • hash值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash的切换。
  • 我们可以使用hashchange事件来监听hash的变化。

触发hsah变化的方式也有两种,一种是通过a标签,并设置href属性,当用户点击这个标签后,URL就会发生改变,也就会触发hashchange事件了:

<a href="#srtian">srtian</a>

还有一种方式就是直接使用JavaScript来对loaction.hash进行赋值,从而改变URL,触发hashchange事件:

location.hash="#srtian"

2 基于History API

前面的hash虽然也很不错,但使用时都需要加上#,并不是很美观。因此到了HTML5,又提供了History API来实现URL的变化。其中做最主要的API有以下两个:history.pushState()和history.repalceState()。

这两个API可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录。此外,这两个api都接受三个参数:

window.history.pushState(null, null, "http://www.163.com");

两种实现方式的对比:基于Hash的路由实现,兼容性更好;而基于History API的路由,则更正式,更美观,可以设置与当前URL同源的任意URL,路径更直观。此外,基于Hash的路由不需要对服务器做改动,基于History API的路由需要对服务器做一些改造,需要对不同的路由进行相应的设置才行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • 准备帮 GIT 申请吉尼斯世界纪录,申请项目是 《人类能够用命令行操作的最为复杂的软件项目》。 《趣谈 | Jav...
    知行社阅读 931评论 0 4
  • 我早年都是用 VIM 写程序,也说不上特別喜欢,主要是当时还不知道怎么退出 VIM 《《深入理解ES6》教程学习笔...
    知行社阅读 799评论 0 7
  • 1,js中this指向的理解 在js面向对象编程里我们避免不了使用this,所以理解this指向对于在面向对象编程...
    orange_9706阅读 201评论 0 0
  • JavaScript执行机制,重点有两点: 1.JavaScript是一门单线程语言。2.Event Loop(事...
    小泡_08f5阅读 11,764评论 2 22
  • 我们从2006年认识,2007年年后表白到现在似乎是12年的时光,2011年嫁给你一晃8年的时间 ,现在的我似乎没...
    葵花子精灵阅读 194评论 0 2