Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8.6 前端面试题目收集(yh) #203

Closed
vieyahn2017 opened this issue Aug 6, 2018 · 29 comments
Closed

8.6 前端面试题目收集(yh) #203

vieyahn2017 opened this issue Aug 6, 2018 · 29 comments

Comments

@vieyahn2017
Copy link
Owner

前端面试题目收集

@vieyahn2017
Copy link
Owner Author

第一期,7.27面试,问的多的:
跨域,继承(原型链),Session/Cookie(sessionStorage,localStorage,cookie)
部分问了websocket
Angularjs(没问几个,面试者并不熟)
vue(之前问vuex,vue-cli,后期问axios,router)
web前端开发,如何提高页面性能优化

@vieyahn2017
Copy link
Owner Author

第二期:8.6面试
问vue,问jquery和vue这类框架的最大区别(前者是DOM,后者是数据双向绑定)
vue的AJAX(用axios,另外问了promise)
另外ES6问let/var,问箭头函数(处理this的问题)
bootstrap响应式设计

@vieyahn2017
Copy link
Owner Author

对于基础我觉得稍微差一点的,会问如下两个入门级的问题:
null/undefined, ==/===
以及两个综合在一起的一个题:
null==undefinded 和 null===undefined 的结果(前者true,后者false)

另:json对象的某个值,能不能是undefined

@vieyahn2017
Copy link
Owner Author

什么是跨域?跨域请求资源的方法有哪些?

1、什么是跨域?
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。存在跨域的情况:
网络协议不同,如http协议访问https协议。
端口不同,如80端口访问8080端口。
域名不同,如qianduanblog.com访问baidu.com。
子域名不同,如abc.qianduanblog.com访问def.qianduanblog.com。
域名和域名对应ip,如www.a.com访问20.205.28.90.
2、跨域请求资源的方法:
(1)、proxy代理
定义和用法:proxy代理用于将请求发送给后台服务器,通过服务器来发送请求,然后将请求的结果传递给前端。
实现方法:通过nginx代理;
注意点:1、如果你代理的是https协议的请求,那么你的proxy首先需要信任该证书(尤其是自定义证书)或者忽略证书检查,否则你的请求无法成功。
(2)、CORS 【Cross-Origin Resource Sharing】
定义和用法:是现代浏览器支持跨域资源请求的一种最常用的方式。
使用方法:一般需要后端人员在处理请求数据的时候,添加允许跨域的相关操作。如下:
res.writeHead(200, {
"Content-Type": "text/html; charset=UTF-8",
"Access-Control-Allow-Origin":'http://localhost',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type'
});
(3)、jsonp
定义和用法:通过动态插入一个script标签。浏览器对script的资源引用没有同源限制,同时资源加载到页面后会立即执行(没有阻塞的情况下)。
特点:通过情况下,通过动态创建script来读取他域的动态资源,获取的数据一般为json格式。
实例如下:

<script> function testjsonp(data) { console.log(data.name); // 获取返回的结果 } </script> <script> var _script = document.createElement('script'); _script.type = "text/javascript"; _script.src = "http://localhost:8888/jsonp?callback=testjsonp"; document.head.appendChild(_script); </script>

缺点:
1、这种方式无法发送post请求(这里)
2、另外要确定jsonp的请求是否失败并不容易,大多数框架的实现都是结合超时时间来判定。

@vieyahn2017
Copy link
Owner Author

javascript面向对象中继承实现?

面向对象的基本特征有:封闭、继承、多态。
在JavaScript中实现继承的方法:

  1. 原型链(prototype chaining)
  2. call()/apply()
  3. 混合方式(prototype和call()/apply()结合)
  4. 对象冒充
    5.【ES6引入了类的一系列概念:class, extends, super】

@vieyahn2017
Copy link
Owner Author

如何理解闭包?

1、定义和用法:当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数内部的其它变量,如果返回的这个函数在外部被执行,就产生了闭包。
2、表现形式:使函数外部能够调用函数内部定义的变量。
3、实例如下:
(1)、根据作用域链的规则,底层作用域没有声明的变量,会向上一级找,找到就返回,没找到就一直找,直到window的变量,没有就返回undefined。这里明显count 是函数内部的flag2 的那个count 。
var count=10; //全局作用域 标记为flag1
function add(){
var count=0; //函数全局作用域 标记为flag2
return function(){
count+=1; //函数的内部作用域
alert(count);
}
}
var s = add()
s();//输出1
s();//输出2

4、变量的作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域分类:全局变量和局部变量。
特点:
1、函数内部可以读取函数外部的全局变量;在函数外部无法读取函数内的局部变量。
2、函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
5、使用闭包的注意点
1)滥用闭包,会造成内存泄漏:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)会改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

@vieyahn2017
Copy link
Owner Author

vieyahn2017 commented Aug 7, 2018

闭包的概念,

我面试之前准备了题目,但是面试的时候并没有问,我怕面试的时候自己hold不住

最近新参考了别处的资料,新建了文档:
(8.7 JavaScript 中 闭包 的详解 )
#205

@vieyahn2017
Copy link
Owner Author

sessionStorage 、localStorage 和 cookie 之间的区别

共同点:用于浏览器端存储的缓存数据
不同点:
(1)、存储内容是否发送到服务器端:当设置了Cookie后,数据会发送到服务器端,造成一定的宽带浪费;
web storage,会将数据保存到本地,不会造成宽带浪费;
(2)、数据存储大小不同:Cookie数据不能超过4K,适用于会话标识;web storage数据存储可以达到5M;
(3)、数据存储的有效期限不同:cookie只在设置了Cookid过期时间之前一直有效,即使关闭窗口或者浏览器;
sessionStorage,仅在关闭浏览器之前有效;localStorage,数据存储永久有效;
(4)、作用域不同:cookie和localStorage是在同源同窗口中都是共享的;sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;

10、Web Storage与Cookie相比存在的优势:
与Cookie相比,Web Storage存在不少的优势,概括为以下几点:
(1)、存储空间更大:IE8下每个独立的存储空间为10M,其他浏览器实现略有不同,但都比Cookie要大很多。
(2)、存储内容不会发送到服务器:当设置了Cookie后,Cookie的内容会随着请求一并发送的服务器,这对于本地存储的数据是一种带宽浪费。而Web Storage中的数据则仅仅是存在本地,不会与服务器发生任何交互。
(3)、更多丰富易用的接口:Web Storage提供了一套更为丰富的接口,使得数据操作更为简便。
(4)、独立的存储空间:每个域(包括子域)有独立的存储空间,各个存储空间是完全独立的,因此不会造成数据混乱。

@vieyahn2017
Copy link
Owner Author

null,undefined 的区别?

null 表示一个对象是“没有值”的值,也就是值为“空”;
undefined 表示一个变量声明了没有初始化(赋值);

undefined不是一个有效的JSON,而null是;
undefined的类型(typeof)是undefined;
null的类型(typeof)是object;

Javascript将未赋值的变量默认值设为undefined;
Javascript从来不会将变量设为null。它是用来让程序员表明某个用var声明的变量时没有值的。

typeof undefined
//"undefined"
undefined :是一个表示"无"的原始值或者说表示"缺少值",就是此处应该有一个值,但是还没有定义。当尝试读取时会返回 undefined;
例如变量被声明了,但没有赋值时,就等于undefined

typeof null
//"object"
null : 是一个对象(空对象, 没有任何属性和方法);
例如作为函数的参数,表示该函数的参数不是对象;

注意:
在验证null时,一定要使用 === ,因为 == 无法分别 null 和 undefined
null == undefined // true
null === undefined // false

@vieyahn2017
Copy link
Owner Author

== 和 ===

(1) "=="叫做相等运算符,"==="叫做严格运算符。
(2) ==,equality -> 等同 的意思, 两边值类型不同的时候,要先进行类型转换为同一类型后,再比较值是否相等。
===,identity -> 恒等 的意思,不做类型转换,类型不同的结果一定不等。
(3) "=="表示只要值相等即可为真,而"==="则要求不仅值相等,而且也要求类型相同。

对于明确数据类型的用===更为可靠,JavaScript是一门弱类型语言,表达式运算赋值等操作都会导致类型转换。而一些隐式转换会带来一些意想不到的后果。
编程建议:尽量使用严格运算符 ===。因为"=="不严谨,可能会带来一些违反直觉的后果。

@vieyahn2017
Copy link
Owner Author

严格运算符 === 的运算规则

严格运算符===的运算规则如下,
(1)不同类型值
如果两个值的类型不同,直接返回false。
(2)同一类的原始类型值
同一类型的原始类型的值(数值number、字符串string、布尔值boolean)比较时,值相同就返回true,值不同就返回false。
(3)同一类的复合类型值/高级类型
两个复合类型(对象Object、数组Array、函数Funtion)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象。即“地址指针”是否相等。
(4)undefined和null
//undefined 和 null 与自身严格相等。
null === null //true
undefined === undefined //true
undefined === null // false

@vieyahn2017
Copy link
Owner Author

相等运算符 "== "的运算规则

相等运算符"=="在比较相同类型的数据时,与严格运算符"==="完全一样。
在比较不同类型的数据时,相等运算符"=="会先将数据进行类型转换,然后再用严格相等运算符"==="比较。类型转换规则如下:
(1)原始类型的值
原始类型的数据会转换成数值类型再进行比较。字符串和布尔值都会转换成数值。
(2)对象与原始类型值比较
对象(这里指广义的对象,包括数值和函数)与原始类型的值比较时,对象转化成原始类型的值,再进行比较。
(3)undefined和null
undefined和null与其他类型的值比较时,结果都为false,它们互相比较时结果为false。
(4)相等运算符"=="的缺点
相等运算符"=="隐藏的类型转换,会带来一些违反直觉的结果。
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true
这就是为什么建议尽量不要使用相等运算符"=="。
至于使用相等运算符"=="会不会对后续代码造成意外影响,答案是有可能会。

【注:以上内容来自网络,但是有部分可能是错的,比如null===undefined的值】

@vieyahn2017
Copy link
Owner Author

假如你写了这样的代码:

function fix(n) {
    if (n == 0) { return n + 1; }
    return n + 2;
}

如果输入n为字符串值"0"的话,恭喜你,你的程序爆炸啦! 你将会得到字符串"01"作为返回值,而不是你想要的数字1。所以一句话概括:没有类型限制,类型转换的后果将是不可预料的。
而且你写的程序很大的话,你可能在这上面浪费好几个小时找 bug。所以在自己需求明确的情况下,为什么不写===来避免可能的 bug 呢?

那么这种不严格比较确实就一无是处吗?
不,比如你想判断一个字符串看起来是不是空白的(由空白字符组成),可以这样写:

if (typeof str === "string" && str == false) {
    console.log("The string is full of white spaces!");
}

因为 ''==false 和' '==false值都是true

@vieyahn2017
Copy link
Owner Author

vieyahn2017 commented Aug 7, 2018

叶欣的 一个题目:

变量声明的提升

@vieyahn2017
Copy link
Owner Author

我们在看一段Code:

var v='Hello World';
(function(){
    alert(v);
})()

经过运行之后,我们发现,还是和我们预期的一样,弹出了“Hello World”。

好了,有意思的来了。接着在看一段下面的代码:

var v='Hello World';
(function(){
    alert(v);
    var v='I love you';
})()

如果这个是一个面试题,面试官问你这个结果是多少?你怎么回答?

我们先看结果吧!

结果是 undefined?和你上面自己想的一样吗?

好吧,我就不故弄玄虚了。其实,这里面隐藏了一个陷阱-----JavaScript中的变量提升(Hoisting);

@vieyahn2017
Copy link
Owner Author

vieyahn2017 commented Aug 7, 2018

现在我来解释下提升是什么意思?顾名思义,就是把下面的东西提到上面。在JS中,就是把定义在后面的东东(变量或函数)提升到前面中定义。

变量提升

变量提升,很简单,就是把变量提升提到函数的top的地方。我么需要说明的是,变量提升 只是提升变量的声明,并不会把赋值也提升上来。

比如:

我们定义三个变量:

(function(){
    var a='One';
    var b='Two';
    var c='Three';
})()

实际上它是这样子的:

(function(){
    var a,b,c;
    a='One';
    b='Two';
    c='Three';
})()

这个时候就把变量提升了呀。

好,我们现在回到第一段code里面。为什么会报错呢?其实,根据我么根据上面变量提升原件以及js的作用域(块级作用域)的分析,得知 上面代码真正变成如下:

var v='Hello World';
(function(){
    var v;
    alert(v);
    v='I love you';
})()

所以,才会提示说“undefined”。

从这里,我们也学习到,我们在写js code 的时候,我么需要把变量放在函数级作用域的顶端,比如我在上面所举的例子:var a,b,c;。以防止出现意外。

@vieyahn2017
Copy link
Owner Author

vieyahn2017 commented Aug 7, 2018

函数提升

函数提升是把整个函数都提到前面去。

在我们写js code 的时候,我们有2中写法,一种是函数表达式,另外一种是函数声明方式。我们需要重点注意的是,只有函数声明形式才能被提升。

函数声明方式提升【成功】

function myTest(){
    foo();
    function foo(){
        console.log("我来自 foo");
    }
}
myTest();

函数表达式方式提升【失败】

function myTest(){
    foo();
    var foo =function foo(){
        console.log("我来自 foo");
    }
}
myTest();

运行结果如下:
VM160:2 Uncaught TypeError: foo is not a function
at myTest (:2:5)
at :7:1

@vieyahn2017
Copy link
Owner Author

vieyahn2017 commented Aug 7, 2018

新看到的一个偏难的题目:

["1", "2", "3"].map(parseInt) 答案是多少?

parseInt() 函数能解析一个字符串,并返回一个整数,需要两个参数 (val, radix),
其中 radix 表示要解析的数字的基数。【该值介于 2 ~ 36 之间,并且字符串中的数字不能大于radix才能正确返回数字结果值】;
但此处 map 传了 3 个 (element, index, array),我们重写parseInt函数测试一下是否符合上面的规则。

function parseInt(str, radix) {
return str+'-'+radix;
};
var a=["1", "2", "3"];
a.map(parseInt); // ["1-0", "2-1", "3-2"] 不能大于radix

因为二进制里面,没有数字3,导致出现超范围的radix赋值和不合法的进制解析,才会返回NaN
所以["1", "2", "3"].map(parseInt) 答案也就是:[1, NaN, NaN]

详细解析:http://blog.csdn.net/justjavac/article/details/19473199

@vieyahn2017
Copy link
Owner Author

vieyahn2017 commented Aug 7, 2018

这个题目,我自己的理解和测试
["1", "2", "3"].map(parseInt)
(3) [1, NaN, NaN]
[1,2,3].map(parseInt)
(3) [1, NaN, NaN]
因为原生的parseInt是两个参数

// 我先写这么一个自己的函数,用内置的arguments看看map调用的时候的参数情况
var parseInt2 = function(i) { console.log(arguments); return i};
[1,2,3].map(parseInt2) 
// map 传了 3 个 (element, index, array),
// 对于map的第一次执行arguments为1, 0, [1,2,3]这样;
//第二次执行是2, 1, [1,2,3];
//第三次执行是3, 2, [1,2,3]

//所以,对于parseInt测试执行,发现:
parseInt(1,0)
1
parseInt(2,1)
NaN
parseInt(3,2)
NaN

所以,["1", "2", "3"].map(parseInt) ,结果是[1, NaN, NaN]也就不奇怪了

@vieyahn2017
Copy link
Owner Author

ES6问let/var,问箭头函数(处理this的问题)
我单独的issue: 在30分钟掌握ES6/ES2015核心内容
#204

要点:
let建立了块级作用域;
箭头函数,最大的作用是,改变了this对象的指向
当我们使用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,它的this是继承外面的,因此内部的this就是外层代码块的this。

关于箭头函数,另外的解释见:
ruanyf/es6tutorial#150

@vieyahn2017
Copy link
Owner Author

另外可以参考这份题目:

https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions/Questions-and-Answers

前端开发面试题

本文由我收集总结了一些前端面试题,初学者阅后也要用心钻研其中的原理,重要知识需要系统学习、透彻学习,形成自己的知识链。万不可投机取巧,临时抱佛脚只求面试侥幸混过关是错误的!也是不可能的!不可能的!不可能的!

面试有几点需注意:(来源寒冬winter 老师,github:@wintercn)

面试题目: 根据你的等级和职位的变化,入门级到专家级,广度和深度都会有所增加。

题目类型: 理论知识、算法、项目细节、技术视野、开放性题、工作案例。

细节追问: 可以确保问到你开始不懂或面试官开始不懂为止,这样可以大大延展题目的区分度和深度,知道你的实际能力。因为这种知识关联是长时期的学习,临时抱佛脚绝对是记不住的。

回答问题再棒,面试官(可能是你面试职位的直接领导),会考虑我要不要这个人做我的同事?所以态度很重要、除了能做事,还要会做人。(感觉更像是相亲( •̣̣̣̣̣̥́௰•̣̣̣̣̣̥̀ ))

资深的前端开发能把absolute和relative弄混,这样的人不要也罢,因为团队需要的是:你这个人具有可以依靠的才能(靠谱)。

前端开发所需掌握知识点概要:

HTML&CSS:
对Web标准的理解(结构、表现、行为)、浏览器内核、渲染原理、依赖管理、兼容性、CSS语法、层次关系,常用属性、布局、选择器、权重、盒模型、
Hack、CSS预处理器、CSS3、Flexbox、CSS Modules、Document flow、BFC、HTML5(离线 & 存储、Histoy,多媒体、WebGL\SVG\Canvas);
JavaScript:
数据类型、运算、对象、Function、继承、闭包、作用域、事件、Prototype、RegExp、JSON、Ajax、DOM、BOM、
内存泄漏、跨域、异步请求、模板引擎、模块化、Flux、同构、算法、ECMAScript6、Nodejs、HTTP、

其他:
主流MVVM框架(React\Vue\Angular)、Hybrid App\React Native\Weex、TypeScript、RESTFul、WEB安全、前端工程化、依赖管理、性能优化、
重构、团队协作、可维护、易用性、SEO、UED、前端技术选型、快速学习能力等;

@vieyahn2017
Copy link
Owner Author

js:防抖动与节流

2017年03月15日 20:15:46
阅读数:11947
针对一些会频繁触发的事件如scroll、resize,如果正常绑定事件处理函数的话,有可能在很短的时间内多次连续触发事件,十分影响性能。

因此针对这类事件要进行防抖动或者节流处理

防抖动

它的做法是限制下次函数调用之前必须等待的时间间隔。正确实现 debouncing 的方法是将若干个函数调用合成 一次,并在给定时间过去之后仅被调用一次。

// 将会包装事件的 debounce 函数
function debounce(fn, delay) {
  // 维护一个 timer
  let timer = null;

  return function() {
    // 通过 ‘this’ 和 ‘arguments’ 获取函数的作用域和变量
    let context = this;
    let args = arguments;

    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  }
}
// 当用户滚动时被调用的函数
function foo() {
  console.log('You are scrolling!');

}

// 在 debounce 中包装我们的函数,过 2 秒触发一次
let elem = document.getElementById('container');
elem.addEventListener('scroll', debounce(foo, 2000));

首先,我们为scroll事件绑定处理函数,这时debounce函数会立即调用,
因此给scroll事件绑定的函数实际上是debounce内部返回的函数

每一次事件被触发,都会清除当前的 timer 然后重新设置超时调用。
这就会导致每一次高频事件都会取消前一次的超时调用,导致事件处理程序不能被触发

只有当高频事件停止,最后一次事件触发的超时调用才能在delay时间后执行
更进一步,我们不希望非要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行。
这里增加一个immediate参数来设置是否要立即执行:

function debouce(func,delay,immediate){
    var timer = null;
    return function(){
        var context = this;
        var args = arguments;
        if(timer) clearTimeout(time);
        if(immediate){
            //根据距离上次触发操作的时间是否到达delay来决定是否要现在执行函数
            var doNow = !timer;
            //每一次都重新设置timer,就是要保证每一次执行的至少delay秒后才可以执行
            timer = setTimeout(function(){
                timer = null;
            },delay);
            //立即执行
            if(doNow){
                func.apply(context,args);
            }
        }else{
            timer = setTimeout(function(){
                func.apply(context,args);
            },delay);
        }
    }
}

节流

节流是另一种处理类似问题的解决方法。
节流函数允许一个函数在规定的时间内只执行一次。

它和防抖动最大的区别就是,节流函数不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。

比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流阀技术来实现。

主要有两种实现方法:

时间戳
定时器
时间戳实现:

var throttle = function(func,delay){
    var prev = Date.now();
    return function(){
        var context = this;
        var args = arguments;
        var now = Date.now();
        if(now-prev>=delay){
            func.apply(context,args);
            prev = Date.now();
        }
    }
}

当高频事件触发时,第一次应该会立即执行(给事件绑定函数与真正触发事件的间隔如果大于delay的话),而后再怎么频繁触发事件,也都是会每delay秒才执行一次。而当最后一次事件触发完毕后,事件也不会再被执行了。

定时器实现:
当触发事件的时候,我们设置一个定时器,再触发事件的时候,如果定时器存在,就不执行;直到delay秒后,定时器执行执行函数,清空定时器,这样就可以设置下个定时器。

var throttle = fucntion(func,delay){
    var timer = null;

    return funtion(){
        var context = this;
        var args = arguments;
        if(!timer){
            timer = setTimeout(function(){
                func.apply(context,args);
                timer = null;
            },delay);
        }
    }
}

当第一次触发事件时,肯定不会立即执行函数,而是在delay秒后才执行。
之后连续不断触发事件,也会每delay秒执行一次。
当最后一次停止触发后,由于定时器的delay延迟,可能还会执行一次函数。

可以综合使用时间戳与定时器,完成一个事件触发时立即执行,触发完毕还能执行一次的节流函数:

var throttle = function(func,delay){
    var timer = null;
    var startTime = Date.now();

    return function(){
        var curTime = Date.now();
        var remaining = delay-(curTime-startTime);
        var context = this;
        var args = arguments;

        clearTimeout(timer);
        if(remaining<=0){
            func.apply(context,args);
            startTime = Date.now();
        }else{
            timer = setTimeout(func,remaining);
        }
    }
}

需要在每个delay时间中一定会执行一次函数,因此在节流函数内部使用开始时间、当前时间与delay来计算remaining,当remaining<=0时表示该执行函数了,如果还没到时间的话就设定在remaining时间后再触发。当然在remaining这段时间中如果又一次发生事件,那么会取消当前的计时器,并重新计算一个remaining来判断当前状态。

总结

防止一个事件频繁触发回调函数的方式:

防抖动:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。

节流:使得一定时间内只触发一次函数。
它和防抖动最大的区别就是,节流函数不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而防抖动只是在最后一次事件后才触发一次函数。
原理是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。

@vieyahn2017
Copy link
Owner Author

web前端开发,如何提高页面性能优化?

内容方面:
1.减少 HTTP 请求 (Make Fewer HTTP Requests)
2.减少 DOM 元素数量 (Reduce the Number of DOM Elements)
3.使得 Ajax 可缓存 (Make Ajax Cacheable)
针对CSS:
1.把 CSS 放到代码页上端 (Put Stylesheets at the Top)
2.从页面中剥离 JavaScript 与 CSS (Make JavaScript and CSS External)
3.精简 JavaScript 与 CSS (Minify JavaScript and CSS)
4.避免 CSS 表达式 (Avoid CSS Expressions)
针对JavaScript:

  1. 脚本放到 HTML 代码页底部 (Put Scripts at the Bottom)
  2. 从页面中剥离 JavaScript 与 CSS (Make JavaScript and CSS External)
  3. 精简 JavaScript 与 CSS (Minify JavaScript and CSS)
  4. 移除重复脚本 (Remove Duplicate Scripts)
    面向图片(Image):
    1.优化图片
    2 不要在 HTML 中使用缩放图片
    3 使用恰当的图片格式
    4 使用 CSS Sprites 技巧对图片优化

@vieyahn2017
Copy link
Owner Author

这边有个参考的帖子;
#206
前端页面性能测试_Yslow评分项详解及优化建议

@vieyahn2017
Copy link
Owner Author

vieyahn2017 commented Aug 7, 2018

你有用过哪些前端性能优化的方法?
(1) 减少http请求次数:CSS Sprites, JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器。
(2) 前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数
(3) 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。
(4) 当需要设置的样式很多时设置className而不是直接操作style。
(5) 少用全局变量、缓存DOM节点查找的结果。减少IO读取操作。
(6) 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。
(7) 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳。
(8) 避免在页面的主体布局中使用table,table要等其中的内容完全下载之后才会显示出来,显示比div+css布局慢。
对普通的网站有一个统一的思路,就是尽量向前端优化、减少数据库操作、减少磁盘IO。向前端优化指的是,在不影响功能和体验的情况下,能在浏览器执行的不要在服务端执行,能在缓存服务器上直接返回的不要到应用服务器,程序能直接取得的结果不要到外部取得,本机内能取得的数据不要到远程取,内存能取到的不要到磁盘取,缓存中有的不要去数据库查询。减少数据库操作指减少更新次数、缓存结果减少查询次数、将数据库执行的操作尽可能的让你的程序完成(例如join查询),减少磁盘IO指尽量不使用文件系统作为缓存、减少读写文件次数等。程序优化永远要优化慢的部分,换语言是无法“优化”的。

@vieyahn2017
Copy link
Owner Author

浏览器是如何渲染页面的?

渲染的流程如下:
1.解析HTML文件,创建DOM树。
自上而下,遇到任何样式(link、style)与脚本(script)都会阻塞(外部样式不阻塞后续外部脚本的加载)。
2.解析CSS。优先级:浏览器默认设置<用户设置<外部样式<内联样式<HTML中的style样式;
3.将CSS与DOM合并,构建渲染树(Render Tree)
4.布局和绘制,重绘(repaint)和重排(reflow)

@vieyahn2017
Copy link
Owner Author

一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?(流程说的越详细越好)

注:这题胜在区分度高,知识点覆盖广,再不懂的人,也能答出几句,
而高手可以根据自己擅长的领域自由发挥,从URL规范、HTTP协议、DNS、CDN、数据库查询、
到浏览器流式解析、CSS规则构建、layout、paint、onload/domready、JS执行、JS API绑定等等;

详细版:
1、浏览器会开启一个线程来处理这个请求,对 URL 分析判断如果是 http 协议就按照 Web 方式来处理;
2、调用浏览器内核中的对应方法,比如 WebView 中的 loadUrl 方法;
  3、通过DNS解析获取网址的IP地址,设置 UA 等信息发出第二个GET请求;
4、进行HTTP协议会话,客户端发送报头(请求报头);
  5、进入到web服务器上的 Web Server,如 Apache、Tomcat、Node.JS 等服务器;
  6、进入部署好的后端应用,如 PHP、Java、JavaScript、Python 等,找到对应的请求处理;
7、处理结束回馈报头,此处如果浏览器访问过,缓存上有对应资源,会与服务器最后修改时间对比,一致则返回304;
  8、浏览器开始下载html文档(响应报头,状态码200),同时使用缓存;
  9、文档树建立,根据标记请求所需指定MIME类型的文件(比如css、js),同时设置了cookie;
  10、页面开始渲染DOM,JS根据DOM API操作DOM,执行事件绑定等,页面显示完成。

简洁版:
浏览器根据请求的URL交给DNS域名解析,找到真实IP,向服务器发起请求;
服务器交给后台处理完成后返回数据,浏览器接收文件(HTML、JS、CSS、图象等);
浏览器对加载到的资源(HTML、JS、CSS等)进行语法解析,建立相应的内部数据结构(如HTML的DOM);
载入解析到的资源文件,渲染页面,完成。

@vieyahn2017
Copy link
Owner Author

vieyahn2017 commented Aug 7, 2018

前端面试题之手写事件模型及事件代理/委托
原文地址:http://www.w3cmark.com/2016/439.html

在前端面试,js是重头戏,也是体现面试者的重要方面。jq库类在前端影响深远,以至于很多入门者直接用jq代替原生js来开发项目,效率是提升了,但是往往面试官为了考察面试者的基础,几乎不可能问你jq里面的某个功能怎么用,而是问你怎么用原生js去实现某个方法或者考察你是否读个jq的源码,是否懂得里面真正的原理。

本文来整理一下关于事件的常被考察的知识点

Q:描述下js里面的事件流

A:DOM2级事件模型中规定了事件流的三个阶段:捕获阶段、目标阶段、冒泡阶段,低版本IE(IE8及以下版本)不支持捕获阶段

捕获事件流:Netscape提出的事件流,即事件由页面元素接收,逐级向下,传播到最具体的元素。

冒泡事件流:IE提出的事件流,即事件由最具体的元素接收,逐级向上,传播到页面。

关于js事件,这里有一篇非常详细的介绍,可以看下:http://www.cnblogs.com/hyaaon/p/4630128.html

Q:IE和W3C不同绑定事件解绑事件的方法有什么区别,参数分别是什么,以及事件对象e有什么区别

A:

绑定事件:

W3C:target.addEventListener(event, listener, useCapture);

event —— 事件类型;listener —— 事件触发时执行的函数;useCapture —— 指定事件是否在捕获或冒泡阶段执行,为true时事件句柄在捕获阶段执行,为false(默认false)时,事件句柄在冒泡阶段执行。

btn.addEventListener('click',function(){
//do something...
},false) 

对应的事件移除:
removeEventListener(event,function,capture/bubble);

IE:target.attachEvent(type, listener);

type - 字符串,事件名称,含“on”,比如“onclick”、“onmouseover”、“onkeydown”等。 listener —— 实现了 EventListener 接口或者是 JavaScript 中的函数。

btn.attachEvent('onclick',function(){
//do something...
})

对应的事件移除:

detachEvent(event,function);

Q:事件的委托(代理 Delegated Events)的原理以及优缺点

A:委托(代理)事件是那些被绑定到父级元素的事件,但是只有当满足一定匹配条件时才会被挪。这是靠事件的冒泡机制来实现的,

优点是:

(1)可以大量节省内存占用,减少事件注册,比如在table上代理所有td的click事件就非常棒

(2)可以实现当新增子对象时无需再次对其绑定事件,对于动态内容部分尤为合适

缺点是:

事件代理的应用常用应该仅限于上述需求下,如果把所有事件都用代理就可能会出现事件误判,即本不应用触发事件的被绑上了事件。

例子:

var toolbar = document.querySelector(".toolbar");
toolbar.addEventListener("click", function(e) {
  var button = e.target;
  if(!button.classList.contains("active"))
    button.classList.add("active");
  else
    button.classList.remove("active");
});

A:其实就是考核对事件对象e的了解程度,以及在IE下对应的属性名。单击button元素会冒泡到UL.toolbar元素,使用了e.target来定位到当前点击的button。

Q:手写原生js实现事件代理,并要求兼容浏览器

/ ============ 
function delegateEvent(interfaceEle, selector, type, fn) {
    if(interfaceEle.addEventListener){
    interfaceEle.addEventListener(type, eventfn);
    }else{
    interfaceEle.attachEvent("on"+type, eventfn);
    }
     
    function eventfn(e){
    var e = e || window.event;    
    var target = e.target || e.srcElement;
    if (matchSelector(target, selector)) {
            if(fn) {
                fn.call(target, e);
            }
        }
    }
}
/**
 * only support #id, tagName, .className
 * and it's simple single, no combination
 */
function matchSelector(ele, selector) {
    // if use id
    if (selector.charAt(0) === "#") {
        return ele.id === selector.slice(1);
    }
    // if use class
    if (selector.charAt(0) === ".") {
        return (" " + ele.className + " ").indexOf(" " + selector.slice(1) + " ") != -1;
    }
    // if use tagName
    return ele.tagName.toLowerCase() === selector.toLowerCase();
}
//调用
var odiv = document.getElementById("oDiv");
delegateEvent(odiv,"a","click",function(){
    alert("1");
})

  

A:大致实现思路就是创建一个类或是匿名函数,在bind和trigger函数外层作用域创建一个字典对象,用于存储注册的事件及响应函数列表,bind时,如果字典没有则创建一个,key是事件名称,value是数组,里面放着当前注册的响应函数,如果字段中有,那么就直接push到数组即可。trigger时调出来依次触发事件响应函数即可。Q:实现事件模型

Q:事件如何派发也就是事件广播(dispatchEvent)

A:一般我们在元素上绑定事件后,是靠用户在这些元素上的鼠标行为来捕获或者触发事件的,或者自带的浏览器行为事件,比如click,mouseover,load等等,有些时候我们需要自定义事件或者在特定的情况下需要触发这些事件。这个时候我们可以使用IE下fireEvent方法,高级浏览器(chrome,firefox等)有dispatchEvent方法。

ie下的例子:

//document上绑定自定义事件ondataavailable
document.attachEvent('ondataavailable', function (event) {
    alert(event.eventType);
});
var obj=document.getElementById("obj");
//obj元素上绑定click事件
obj.attachEvent('onclick', function (event) {
alert(event.eventType);
});
//调用document对象的createEventObject方法得到一个event的对象实例。
var event = document.createEventObject();
event.eventType = 'message';
//触发document上绑定的自定义事件ondataavailable
document.fireEvent('ondataavailable', event);
//触发obj元素上绑定click事件
document.getElementById("test").onclick = function () {
    obj.fireEvent('onclick', event);
};

高级浏览器(chrome,firefox等)的例子:

//document上绑定自定义事件ondataavailable
document.addEventListener('ondataavailable', function (event) {
    alert(event.eventType);
}, false);
var obj = document.getElementById("obj");
//obj元素上绑定click事件
obj.addEventListener('click', function (event) {
    alert(event.eventType);
}, false);
//调用document对象的 createEvent 方法得到一个event的对象实例。
var event = document.createEvent('HTMLEvents');
// initEvent接受3个参数:
// 事件类型,是否冒泡,是否阻止浏览器的默认行为
event.initEvent("ondataavailable", true, true);
event.eventType = 'message';
//触发document上绑定的自定义事件ondataavailable
document.dispatchEvent(event);
var event1 = document.createEvent('HTMLEvents');
event1.initEvent("click", true, true);
event1.eventType = 'message';
//触发obj元素上绑定click事件
document.getElementById("test").onclick = function () {
    obj.dispatchEvent(event1);
};

@104gogo
Copy link

104gogo commented Dec 10, 2018

老哥,为什么close了啊?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants