5

以下问题解释非本人原创,是根据面试经验整理后觉得更容易理解的解释版本,欢迎补充


一. 输入url后的加载过程

从输入 URL 到页面加载完成的过程中都发生了什么

计算机网络体系结构

  • 应用层(HTTP、SMTP、FTP、POP3)

  • 运输层(TCP、UDP)

  • 网络层(IP(路由器))

  • 数据链路层(网桥(CSMA/CD、PPP))

  • 物理层(集线器)

1. 查找域名对应IP地址

这一步包括 DNS 具体的查找过程,包括:浏览器缓存->系统缓存->路由器缓存...
(1) 浏览器搜索自己的 DNS 缓存(维护一张域名与 IP 地址的对应表);
(2) 搜索操作系统中的 DNS 缓存(维护一张域名与 IP 地址的对应表);
(3) 搜索操作系统的 hosts 文件( Windows 环境下,维护一张域名与 IP 地址的对应表);
(4) 操作系统将域名发送至 LDNS(本地区域名服务器),LDNS 查询 自己的 DNS 缓存(一般查找成功率在 80% 左右),查找成功则返回结果,失败则发起一个迭代 DNS 解析请求

① LDNS 向 Root Name Server (根域名服务器,如 com、net、org等的解析的顶级域名服务器的地址)发起请求,此处,Root Name Server 返回 com 域的顶级域名服务器的地址;

② LDNS 向 com 域的顶级域名服务器发起请求,返回 baidu.com 域名服务器地址;

③ LDNS 向 baidu.com 域名服务器发起请求,得到 www.baidu.com 的 IP 地址;

(5) LDNS 将得到的 IP 地址返回给操作系统,同时自己也将 IP 地址缓存起来;

(6) 操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来;

2. 建立连接(TCP的三次握手)

(1) 主机向服务器发送一个建立连接的请求;

(2) 服务器接到请求后发送同意连接的信号;

(3) 主机接到同意连接的信号后,再次向服务器发送了确认信号 ;

注意:这里的三次握手中主机两次向服务器发送确认,第二次是为了防止已失效的连接请求报文段传至服务器导致错误。

3. 构建网页

(1) 浏览器根据 URL 内容生成 HTTP 请求,请求中包含请求文件的位置、请求文件的方式等等;

(2) 服务器接到请求后,会根据 HTTP 请求中的内容来决定如何获取相应的 HTML 文件;

(3) 服务器将得到的 HTML 文件发送给浏览器;

(4) 在浏览器还没有完全接收 HTML 文件时便开始渲染、显示网页;

(5) 在执行 HTML 中代码时,根据需要,浏览器会继续请求图片、音频、视频、CSS、JS等文件,过程同请求 HTML ;

浏览器渲染展示网页过程

  1. HTML代码转化为DOM(DOM Tree)

  2. CSS代码转化成CSSOM(CSS Object Model)

  3. 结合DOM和CSSOM,生成一棵渲染树(包含每个节点的视觉信息)(Render Tree)

  4. 生成布局(layout),即将所有渲染树的所有节点进行平面合成

  5. 将布局绘制(paint)在屏幕上

4. 断开连接(TCP的四次挥手)

(1) 主机向服务器发送一个断开连接的请求;

(2) 服务器接到请求后发送确认收到请求的信号;(此时服务器可能还有数据要发送至主机)

(3) 服务器向主机发送断开通知;(此时服务器确认没有要向主机发送的数据)

(4) 主机接到断开通知后断开连接并反馈一个确认信号,服务器收到确认信号后断开连接;

注意:这里的四次挥手中服务器两次向主机发送消息,第一次是回复主机已收到断开的请求,第二次是向主机确认是否断开,确保数据传输完毕。

三次握手 && 四次挥手


二. 有了解过Common.js吗?

发现这方面的资料很少啊,都没有相中的比较好理解的。

浅析JS中的模块规范(CommonJS,AMD,CMD)

前端模块化(CommonJs,AMD和CMD)

前端模块化

前端模块化

三. 有了解过React.js吗?

React.js 只是一个视图库

  (1)声明式设计

  (2)高效:通过对DOM的模拟,最大限度的减少与DOM的交互。

  (3)灵活:可以与已知的框架或库很好的配合。

  (4)JSX:是js语法的扩展,不一定使用,但建议用。

  (5)组件:构建组件,使代码更容易得到复用,能够很好地应用在大项目的开发中。

  (6)单向响应的数据流:React实现了单向响应的数据流,从而减少了重复代码,这也是解释了它为什么比传统数据绑定更简单。

react 通过prop管理组件通信,通过state 驱动视图比较差异进行更新操作

作者:第七页
链接:https://www.zhihu.com/question/39825457/answer/83544390
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

*angular 是MV 框架, react是用来构建可重复使用的UI组件的, 可以看成是个提供工具的library。
react 可以和 angular 的 directive做比较。 这样比较的话, react是比angular directive 在组建UI上更powerful的。**

作者:空空
链接:https://www.zhihu.com/question/23444167/answer/24957302
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

React.js 初学者应该知道的 9 件事

React之特点及常见用法

React 入门实例教程

请问 React 和 Angular 各有什么优缺点,各自又适合什么开发场景?


四. angular和vue的区别

唉,这个真的太难总结了,求评论!!!

Vue拥有类似 Angular 的双向数据绑定,以及类似 React 的虚拟DOM。

<div class="image-package">

angular && vue && react

Vue.js 很好,但会比 Angular 或 React 更好吗?

vue.js与angular,react等框架分析比较

浅析angular,react,vue.js jQuery使用区别

    • *

五. less的特点

每次被问到这个我只能想起less中的定义变量,用太久less都忘了css不能嵌套,醉了醉了。

  1. 变量

  2. 混合(Mixins)

  3. 嵌套规则

  4. 运算

  5. 函数

  6. 命名空间

  7. 作用域

  8. 注释

  9. 导入(Import)

Less语言特性

sass-vs-less

Sass和Less的区别

sass 与 less 的区别与学习


六. less的原理

本质上,less 包含一套自定义的语法及一个解析器,用户根据这些语法定义自己的样式规则,这些规则最终会通过解析器,less 把这些样式规则编译成浏览器可以识别的 css 样式。less 并没有裁剪 css 原有的特性,更不是用来取代 css 的,而是在现有 css 语法的基础上,为 css 加入程序式语言的特性。less 最终需要编译成 css 文件才能起到样式的效果,我们可以称 less 为 css 样式生成工具。


七. gulp的特点

  1. 使用 gulp.js,你的构建脚本是代码,而不是配置文件;

  2. 使用标准库(node.js standard library)来编写脚本;

  3. 插件都很简单,只负责完成一件事-基本上都是 20 行左右的函数;

  4. 任务都以最大的并发数来执行;

  5. Gulp是一个基于流的构建系统,使用代码优于配置的策略。输入/输出(I/O)是基于“流式”的。

作用

  1. Sass、Less编译

  2. Css Js 图片压缩

  3. Css Js 合并

  4. Css Js 内联

  5. Html的include功能

  6. Autoprefixer(自动处理浏览器css前缀)

  7. 自动刷新

  8. 去缓存

  9. Handlebars模板文件的预编译

  10. 雪碧图

  11. ESLint

  12. rem移动端适配方案

其他补充

gulp grunt
速度
格式 和node差不多 json套json
操作基于 二进制流 文件

gulp VS grunt

前端工程的构建工具对比 Gulp vs Grunt

模块化 && 构建

dist是指distribution——分配,分发——发布完成的文件夹一般命名dist。

dest则是destination——目的地,终点——用于grunt文件路径相关的配置项,一般会和src配对出现。比如文件压缩插件:压缩源(src)文件,生成压缩包到(dest)。

作者:峰子
链接:https://www.zhihu.com/question/29199796/answer/82862432
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

常规GruntFile.js && gulpFile.js

  • GruntFile.js

<script>
    module.exports = function(grunt) {
        // 导入模块
        grunt.loadNpmTasks('grunt-contrib-uglify');
        grunt.loadNpmTasks('grunt-contrib-cssmin');
        grunt.loadNpmTasks('grunt-contrib-htmlmin');
        grunt.loadNpmTasks('grunt-contrib-imagemin');
        grunt.loadNpmTasks('grunt-contrib-watch');

        // 配置任务
        grunt.initConfig({
            // js压缩 默认加密压缩
            uglify: { // 主任务名称
                options: { // [配置选项]
                    mangle: false // 是否加密压缩
                },
                a: { // 子任务名称
                    expand: true, // 是否分开压缩
                    src: 'js/*.js',    // 源文件
                    dest: 'build'    // 目标文件 自动创建源文件文件夹
                }
            },
            cssmin: {
                a: {
                    expand: true,
                    src: 'css/*.css',
                    dest: 'build'
                }
            },
            htmlmin: {
                options: {
                    removeComments: true,    // 是否移除注释
                    collapseWhitespace: false    // 是否去掉空白
                },
                a: {
                    src: '*.html',
                    dest: 'build'
                }
            },
            // imagemin: {
            //     a: {
            //         expand: true, //分开执行
            //         cwd: 'images',
            //         src: ['**/*.{png,jpg}'],
            //         dest: 'build/images'
            //     }
            // },
            watch: {
                a: {
                    files: ['*.html', 'css/*.css', 'js/*.js'],
                    tasks: ['cssmin', 'htmlmin', 'uglify']
                }
            }
        });

        // 注册一个默认任务
        grunt.registerTask('default', ['uglify', 'cssmin', 'htmlmin', 'watch']);
    }
</script>
  • gulpfile.js

<script>
    // 导入模块
    var gulp = require('gulp');
    var cssmin = require('gulp-cssmin');
    var uglify = require('gulp-uglify');
    var htmlmin = require('gulp-htmlmin');
    var concat = require('gulp-concat');
    var rename = require('gulp-rename');    // 改名

    // 配置任务
    gulp.task('uglify:css', function() {
        gulp.src('css/*.css')
            .pipe(cssmin())        // 压缩
            .pipe(concat('all.min.css'))    // 合并
            .pipe(gulp.dest('build/css'))    // 输出
    });
    gulp.task('uglify:js', function() {
        gulp.src('js/*.js')
            .pipe(uglify())                    // 压缩
            .pipe(gulp.dest('build/js'))    // 输出
    });
    gulp.task('uglify:html', function() {
        gulp.src('*.html')
            .pipe(htmlmin({                    // 压缩
                collapseWhitespace: true,
                removeComments: true
            }))
            .pipe(gulp.dest('build'))        // 输出
    });

    gulp.watch('*.*', ['uglify:css', 'uglify:js', 'uglify:html']);

    gulp.task('default', ['uglify:css', 'uglify:js', 'uglify:html']);
</script>
<script>
    var gulp = require('gulp');
    var uglify = require('gulp-uglify');
    var clean = require('gulp-clean-css');
    var sass = require('gulp-sass');

    gulp.task('uglify',function(){
        return(
            gulp.src('./src/*.js')
                .pipe(uglify())
                .pipe(gulp.dest('dist'))
        )
    })

    gulp.task('minify-css',function(){
        return (
            gulp.src('./src/*.css')
                .pipe(clean())
                .pipe(gulp.dest('dist'))
        )
    })

    gulp.task('compile-sass',function(){
        return (
            gulp.src('./src/*.scss')
                .pipe(sass().on('error', sass.logError))
                .pipe(gulp.dest('dist'))
        )
    })

    gulp.task('default',function(){
        gulp.watch('./src/*.js',['uglify']);
        gulp.watch('./src/*.css',['minify-css']);
        gulp.watch('./src/*.scss',['compile-sass']);
    })
</script>
    • *

八. ajax的原理

  • 在旧的交互方式中,由用户触发一个HTTP请求到服务器,服务器对其进行处理后再返回一个新的HTHL页到客户端, 每当服务器处理客户端提交的请求时,客户都只能空闲等待,并且哪怕只是一次很小的交互、只需从服务器端得到很简单的一个数据,都要返回一个完整的HTML页,而用户每次都要浪费时间和带宽去重新读取整个页面。而使用Ajax后用户从感觉上几乎所有的操作都会很快响应没有页面重载(白屏)的等待。

  • Ajax的原理简单来说是在用户和服务器之间加了—个中间层(AJAX引擎),通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。使用户操作与服务器响应异步化。这其中最关键的一步就是从服务器获得请求数据。

    • Ajax的过程只涉及JavaScript、XMLHttpRequest和DOM。XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是javascript可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。

每天10个前端知识点:ajax && jsonp

ajax过程

  1. 获得ajax

  2. 打开地址

  3. 发送数据

  4. 接收数据

<script>
 // 1.获得ajax
 if (window.XMLHttpRequest) { //查看当前浏览器XMLHttpRequest是否是全局变量
     var oAjax = new XMLHttpResquest();
 } else {
     var oAjax = new ActiveXObject('Microsoft.XMLHTTP'); //IE6,传入微软参数
 }

 // 2.打开地址
 switch (json.type.toLowerCase()) {
     case 'get':
         oAjax.open('GET', json.url + '?' + jsonToURL(json.data), true); // 提交方式(大写),url,是否异步
         oAjax.send(); // 3.发送数据
         break;
     case 'post':
         oAjax.open('POST', json.url, true);
         oAjax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
         oAjax.send(jsonToURL(json.data)); // 3.发送数据
         break;
 }

 // 4.接收数据
 oAjax.onreadystatechange = function() { //监控状态
     if (oAjax.readyState == 4) {
         json.complete && json.complete();
         if (oAjax.status >= 200 && oAjax.status < 300 ||
             oAjax.status == 304) {
             json.success && json.success(oAjax.responseText); //执行成功的回调函数, responseText为响应内容
         } else {
             json.error && json.error(oAjax.status); //执行失败的回调函数
         }
     }
 };
</script>
    • *

九. 有了解过ES6吗?

哈哈,这个后面会写一整篇,敬请期待!

【探秘ES6】系列专栏(一):ES6简介


十. git merge

git merge 和 git rebase 小结


十一. less不依靠构建转css

我猜这道面试题应该也不让用 lessc ,哈哈哈!

依旧求评论,我只会lessc和构建转化诶。

甩上gulp构建转化

<script>
    var gulp = require('gulp'),
        less = require('gulp-less');

    gulp.task('testLess', function() {
        gulp.src(['src/less/index.less', 'src/less/detail.less']) //多个文件以数组形式传入
            .pipe(less())
            .pipe(gulp.dest('src/css')); //将会在src/css下生成index.css以及detail.css 
    });

    gulp.task('testWatch', function() {
        gulp.watch('src/**/*.less', ['testLess']); //当所有less文件发生改变时,调用testLess任务
    });
</script>

十二. 冒泡、快排

这个后面也有一篇简单的算法篇,敬请期待!

  • 冒泡排序

    每次比较相邻的两个数,如果后一个比前一个小,换位置。
    时间复杂度:O(n^2)
    
<script>
    var arr = [3, 1, 4, 6, 5, 7, 2];

    function bubbleSort(arr) {
        var len = arr.length;
        for (var i = len; i >= 2; --i) {
            for (var j = 0; j < i - 1; j++) {
                if (arr[j + 1] < arr[j]) {
                    var temp;
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        return arr;
    }

    function bubbleSort2(arr) {
        var len = arr.length;
        for (var i = 0; i <= len - 1; i++) {
            for (var j = 0; j <= len - i; j++) {
                if (arr[j + 1] < arr[j]) {
                    var temp;
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        return arr;
    }

    console.log(bubbleSort(arr));
    console.log(bubbleSort2(arr));
</script>
  • 快速排序

    采用二分法,取出中间数,数组每次和中间数比较,小的放到左边,大的放到右边。
    时间复杂度:O(nlog2(n))
    
<script>
    var arr = [3, 1, 4, 6, 5, 7, 2];

    function quickSort(arr) {
        if(arr.length == 0) {
            return [];    // 返回空数组
        }

        var cIndex = Math.floor(arr.length / 2);
        var c = arr.splice(cIndex, 1);
        var l = [];
        var r = [];

        for (var i = 0; i < arr.length; i++) {
            if(arr[i] < c) {
                l.push(arr[i]);
            } else {
                r.push(arr[i]);
            }
        }

        return quickSort(l).concat(c, quickSort(r));
    }

    console.log(quickSort(arr));
</script>

十三. promise

Promise对象有以下两个特点。

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

ECMAScript 6入门 - Promise对象


十四. 性能优化

详见性能优化篇!

每天10个前端知识点:性能优化篇 - 简书


十五. js的冒泡(Bubbling Event)和捕获(Capture Event)的区别

js之事件冒泡和事件捕获详细介绍

  1. 冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。

  2. 捕获型事件(event capturing):事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定)。

  3. DOM事件流:同时支持两种事件模型:捕获型事件和冒泡型事件,但是,捕获型事件先发生。两种事件流会触及DOM中的所有对象,从document对象开始,也在document对象结束。

    DOM事件模型最独特的性质是,文本节点也触发事件(在IE中不会)。
    

示例
假设一个元素div,它有一个下级元素p。

<div>
  <p>元素</p>
</div>

这两个元素都绑定了click事件,如果用户点击了p:

  • 事件捕获

    当你使用事件捕获时,**父级元素先触发**,子级元素后触发,即div先触发,p后触发。
  • 事件冒泡

    当你使用事件冒泡时,**子级元素先触发**,父级元素后触发,即p先触发,div后触发。
    

addEventListener函数,它有三个参数,第三个参数若是true,则表示采用事件捕获,若是false,则表示采用事件冒泡。
IE只支持事件冒泡,不支持事件捕获。

Paste_Image.png

阻止冒泡

• 在W3c中,使用stopPropagation()方法
• 在IE下设置oEvent.cancelBubble = true

在捕获的过程中stopPropagation()后,后面的冒泡过程也不会发生了。

阻止捕获

阻止事件的默认行为,例如click <a>后的跳转
• 在W3c中,使用oEvent.preventDefault()方法;
• 在IE下设置window.event.returnValue = false;


十六. 数组的翻转(非reverse)

<script>
    var arr=[1,2,3,4];
    var arr2=[];
    while(arr.length) {
        var num=arr.pop();
        arr2.push(num);
    }
    alert(arr2);
</script>

数组更多应用详见:每天10个前端知识点:数组应用篇


十七. js的Object和其他语言的object的区别

js对象的创建 js对象和java对象的不同

面向对象分为基于原型的面向对象和基于模板的面向对象。

  • Java:基于模板的面向对象。

class A
{
   private String name;
   public void fun(){

   }
}

A a = new A();
a.fun();
  • JavaScript:基于原型的面向对象,基于事件的网页脚本语言。

<script>
    function CreateObject() {

    }

    CreateObject.prototype = {
        constructor: CreateObject,  // 可特意声明constructor指向 CreateObject
        name: 'xxx',
        age: '11',
        children: ['aaa', 'bbb'],
        getName: function() {
            return this.name;
        }
    }

    var p = new CreateObject();
    console.log(p.name); // 'xxx'
</script>

十八. js的Element和Node的区别

JavaScript中Element与Node的区别,children与childNodes的区别

  • Element继承了Node类,也就是说Element是Node多种类型中的一种。

  • children是Element的属性,childNodes是Node的属性

HTML中的Node和Element的区别

  • NODE是相对TREE这种数据结构而言的。TREE就是由NODE组成。

    node有几个**子类型**:**Element**,Text,,Attribute,RootElement,Comment,Namespace等。
  • ELEMENT则是HTML里的概念,是元素即标签包含内部属性及内容的总体,是HTML中的数据的组成部分之一。

    • *

十九. svn与git的区别

  1. git是分布式的,svn不是。

    git跟svn一样有自己的集中式版本库或服务器。但git更倾向于被使用于分布式模式,克隆版本库后即使没有网络也能够commit文件,查看历史版本记录,创建项目分支等,等网络再次连接上Push到服务器端。
    
  2. git把内容按元数据方式存储,而svn是按文件。

    所有的资源控制系统都是把文件的元信息隐藏在一个类似.svn,.cvs等的文件夹里。
    git目录是处于你的机器上的一个克隆版的版本库,它拥有中心版本库上所有的东西,例如标签,分支,版本记录等。
    
  3. git没有一个全局的版本号,svn有。

  4. git的内容完整性优于svn。

    因为git的内容存储使用的是SHA-1哈希算法。
    
  5. git可以有无限个版本库,svn只能有一个指定中央版本库。

    当svn中央版本库有问题时,所有工作成员都一起瘫痪直到版本库维修完毕或者新的版本库设立完成。
    每一个git都是一个版本库,区别是它们是否拥有活跃目录(Git Working Tree)。如果主要版本库(例如:置於GitHub的版本库)有问题,工作成员仍然可以在自己的本地版本库(local repository)提交,等待主要版本库恢复即可。工作成员也可以提交到其他的版本库!
    

二十. 定时器

这是一道笔试题,小白就是小白啊,还第一次见到定时器的第三个参数,还是这么写的。

你应该知道的setTimeout秘密

↑ 上面这篇文章值得耐心细看,对理解定时器有很大帮助。

首先我们要了解之前提到的定时器,setTimeout(fn, 0)输出时间并不为0且大于0。

这是因为 JavaScript是单线程执行的。也就是说,在任何时间点,有且只有一个线程在运行JavaScript程序,无法同一时候运行多段代码。

浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。

  • JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。

  • GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。

  • 事件触发线程,当一个事件被触发时,该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系,所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。


以下为这次遇见的题目

广义上我们遇到定时器的题目一般是这样的

setTimeout

<script>
    for (var i = 1; i < 4; i++) {
        var t = setTimeout(function() {
            console.log(i);  // 2\. 输出三次4
        }, 10);
    }
    console.log(i);  // 1\. 4
</script>

这次遇见的是这样的:

循环只进行两次

<script>
    for (var i = 1; i < 4; i++) {
        var t = setTimeout(function(i) {
            console.log(i);  // 2\. 1  4.2
            console.log(t);  // 3\. 3  5.3
            clearTimeout(t);
        }, 10, i);
    }
    console.log(i);  // 1\. 4
</script>

关于定时器.png

几个让我印象深刻的面试题(一) | Jay Zangwill

请注意:这个t是定义在闭包外面的,也就是说t并没有被闭包保存,所以这里的t指的是最后一个循环留下来的t,所以最后一个3被清除了,没有输出。

setTimeout可以传入第三个参数、第四个参数...,用来表示第一个参数(回调函数)传入的参数。

于是我检测了以下变形

循环只进行两次

<script>
    for (var i = 1; i < 4; i++) {
        var t = setTimeout(function(i) {
            console.log(i);  // 2\. 2  4\. 2
            console.log(t);  // 3\. 3  5\. 3
            clearTimeout(t);
        }, 10, 2);
    }
    console.log(i);  // 1\. 4
</script>

循环只进行两次

<script>
    for (var i = 1; i < 4; i++) {
        var t = setTimeout(function(i, t) {
            console.log(i);  // 2\. 2  4\. 2
            console.log(t);  // 3\. 3  5\. 3
            clearTimeout(t);
        }, 10, 2, 3);  // 当t传入 2 / 3 时都是只进行两次循环
    }
    console.log(i);  // 1\. 4
</script>

t为非 2 / 3 时,循环输出3次

<script>
    for (var i = 1; i < 4; i++) {
        var t = setTimeout(function(i, t) {
            console.log(i);  // 2\. 2  4\. 2  6\. 2
            console.log(t);  // 3\. 4  5\. 4  7\. 4
            clearTimeout(t);
        }, 10, 2, 4);
    }
    console.log(i);  // 1\. 4
</script>
<script>
    for (var i = 1; i < 4; i++) {
        var t = setTimeout(function(i, t) {
            console.log(i); // 2\. 1  4\. 2  6\. 3
            console.log(t); // 3\. undefined  5\. 1  7\. 2
            clearTimeout(t);
        }, 10, i, t);
    }
    console.log(i); // 1\. 4
</script>

setInterval

<script>
    for (var i = 0; i < 4; i++) {
        var t = setInterval(function() {
            console.log(i);  // 2\. 一直输出4
        }, 10);
    }
    console.log(i);  // 1\. 4
</script>

这次遇见的长这样:

<script>
    for (var i = 0; i < 4; i++) {
        var t = setInterval(function(i, t) {
            console.log(i);  // 2\. 0,1,2,3,3,3,...
            clearInterval(t);
        }, 10, i, t);
    }
    console.log(i);  // 1\. 4
</script>

几个让我印象深刻的面试题(一) | Jay Zangwill

第一次触发回调函数执行的时候 t 的值是undefined,第二次触发的时候有值了。这和程序的执行顺序有关。我们知道js正常情况下是从上到下,从右到左执行的。
所以这里每次循环先设置定时器,然后把定时器的返回值赋值给t。在第一次循环的时候t并没有被赋值,所以是undefined,在第二次循环的时候,定时器其实清理的是上一个循环的定时器。所以导致每次循环都是清理上一次的定时器,而最后一次循环的定时器没被清理,导致一直输出3。

于是我做了以下测试

<script>
    for (var i = 1; i < 4; i++) {
        var t = setInterval(function(i, t) {
            console.log(i);  // 2\. 2  4\. 2  6\. 2...
            console.log(t);  // 3\. 3  5\. 3  7\. 3...
            clearInterval(t);
        }, 10, 2, 3);
    }
    console.log(i);  // 1\. 4
</script>
<script>
    for (var i = 0; i < 4; i++) {
        var t = setInterval(function(i, t) {
            console.log(i);  // 2\. 0  4\. 1   6\. 2  8\. 3  10\. 3...
            console.log(t); // 3\. undefined  5\. 1  7\. 2  9\. 3  11\. 3...
            clearInterval(t);
        }, 10, i, t);
    }
    console.log(i); // 1\. 4
</script>

转载:http://www.jianshu.com/p/3944...

个人建了前端学习群,旨在一起学习前端。纯净、纯粹技术讨论,非前端人员勿扰!入群加我微信:iamaixiaoxiao。


wuli笑笑
152 声望5 粉丝