2017拼多多前端笔试

简答题:

settimeout 与 setInterval的区别, 及对他们的内存的分析

区别

  1. setTimeout是在一段时间后调用指定函数(仅一次)javascript

  2. setInterval是每隔一段时间调用指定函数(N次)php

function run(){
    // 其余代码
    setTimeout(function(){
        run();
    }, 10000);
}
run();

以上面的代码来讲, 虽然设置的是10s执行一次, 可是实际时间倒是须要// 其余代码的执行时间来肯定
即setTimeout的间隔时间是, // setTimeout 的间隔时间 === 最小时间是(10s+)html

setInterval(function(){
    run();
}, 10000);

而setInterval, 不会有上面的问题, 可是若是run()的执行时间, 操做大于10s, 那么甚至可能跳过任务;java

setInterval 和 setTimeout 会产生内存溢出
JavaScript setInterval()方法是否致使内存泄漏?node

关于内存泄漏

内存

程序的运行须要内存。只要程序提出要求,操做系统或者运行时(runtime)就必须供给内存。
对于持续运行的服务进程(daemon),必须及时释放再也不用到的内存。不然,内存占用愈来愈高,轻则影响系统性能,重则致使进程崩溃。
再也不用到的内存,没有及时释放,就叫作内存泄漏(memory leak)。
(好比 C 语言)必须手动释放内存,程序员负责内存管理。git

char * buffer;
buffer = (char*) malloc(42);

//...

free(buffer)    //手动释放内存

上面是 C 语言代码,malloc方法用来申请内存,使用完毕以后,必须本身用free方法释放内存。
这很麻烦,因此大多数语言提供自动内存管理,减轻程序员的负担,这被称为"垃圾回收机制"(garbage collector)。程序员

垃圾回收机制

怎么知道哪些内存再也不须要呢?经常使用的方法是 '引用计数', 语言的引擎有一张 '引用表', 保存了内存里面全部的资源(一般是各类值)的引用次数,当一个值的引用次数为 0 时,表示这个值用不到了,所以可将其释放。github

可是若是一个值再也不用到了,引用次数却不为 0 ,垃圾回收机制却没法释放这块内存,从而致使内存泄漏。面试

const arr = [1, 2, 3, 4];
console.log(arr);

打印完 arr 以后, arr 便用不到了,引用次数为 1, 可是它还会继续占用内存。ajax

const arr = [1, 2, 3, 4];
console.log(arr);
arr = null;

arr 重置为 null,就解除了对 [1, 2, 3, 4] 的引用,引用次数变成了 0 ,内存就能够释放了。

JavaScript 内存管理

JavaScript 是一种垃圾回收语言。垃圾回收语言经过周期性地检查先前分配的内存是否可达,帮助开发者管理内存。换言之,垃圾回收语言减轻了“内存仍可用”及“内存仍可达”的问题。二者的区别是微妙而重要的:仅有开发者了解哪些内存在未来仍会使用,而不可达内存经过算法肯定和标记,适时被操做系统回收。

JavaScript 内存泄漏

垃圾回收语言的内存泄漏主因是不须要的引用。理解它以前,还需了解垃圾回收语言如何辨别内存的可达与不可达。

Mark-and-sweep

  1. 大部分垃圾回收语言用的算法称之为 Mark-and-sweep 。算法由如下几步组成:
    垃圾回收器建立了一个“roots”列表。Roots 一般是代码中全局变量的引用。JavaScript 中,“window” 对象是一个全局变量,被看成 root 。window 对象老是存在,所以垃圾回收器能够检查它和它的全部子对象是否存在(即不是垃圾);

  2. 全部的 roots 被检查和标记为激活(即不是垃圾)。全部的子对象也被递归地检查。从 root 开始的全部对象若是是可达的,它就不被看成垃圾。

  3. 全部未被标记的内存会被当作垃圾,收集器如今能够释放内存,归还给操做系统了。

现代的垃圾回收器改良了算法,可是本质是相同的:可达内存被标记,其他的被看成垃圾回收。

不须要的引用是指开发者明知内存引用再也不须要,却因为某些缘由,它仍被留在激活的 root 树中。在 JavaScript 中,不须要的引用是保留在代码中的变量,它再也不须要,却指向一块本该被释放的内存。有些人认为这是开发者的错误。

为了理解 JavaScript 中最多见的内存泄漏,咱们须要了解哪一种方式的引用容易被遗忘。

常见 JavaScript 内存泄漏

意外的全局变量

JavaScript 处理未定义变量的方式比较宽松:未定义的变量会在全局对象建立一个新变量。在浏览器中,全局对象是 window 。

function foo(arg) {
    bar = "this is a hidden global variable";
}

真相是:

function foo(arg) {
    window.bar = "this is an explicit global variable";
}

函数 foo 内部忘记使用 var ,意外建立了一个全局变量。此例泄漏了一个简单的字符串,无伤大雅,可是有更糟的状况。

另外一种意外的全局变量可能由 this 建立:

function foo() {
    this.variable = "potential accidental global";
}
// Foo 调用本身,this 指向了全局对象(window)
// 而不是 undefined
foo();

在 JavaScript 文件头部加上 'use strict',能够避免此类错误发生。启用严格模式解析 JavaScript ,避免意外的全局变量。

全局变量注意事项:
尽管咱们讨论了一些意外的全局变量,可是仍有一些明确的全局变量产生的垃圾。它们被定义为不可回收(除非定义为空或从新分配)。尤为当全局变量用于临时存储和处理大量信息时,须要多加当心。若是必须使用全局变量存储大量数据时,确保用完之后把它设置为 null 或者从新定义。与全局变量相关的增长内存消耗的一个主因是缓存。缓存数据是为了重用,缓存必须有一个大小上限才有用。高内存消耗致使缓存突破上限,由于缓存内容没法被回收。

被遗忘的计时器或回调函数

在 JavaScript 中使用 setInterval 很是日常。一段常见的代码:

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        // 处理 node 和 someResource
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);

此例说明了什么:与节点或数据关联的计时器再也不须要,node 对象能够删除,整个回调函数也不须要了。但是,计时器回调函数仍然没被回收(计时器中止才会被回收)。同时,someResource 若是存储了大量的数据,也是没法被回收的。

对于观察者的例子,一旦它们再也不须要(或者关联的对象变成不可达),明确地移除它们很是重要。老的 IE 6 是没法处理循环引用的。现在,即便没有明确移除它们,一旦观察者对象变成不可达,大部分浏览器是能够回收观察者处理函数的。

观察者代码示例:

var element = document.getElementById('button');
function onClick(event) {
    element.innerHTML = 'text';
}
element.addEventListener('click', onClick);   // => 循环调用

对象观察者和循环引用注意事项
老版本的 IE 是没法检测 DOM 节点与 JavaScript 代码之间的循环引用,会致使内存泄漏。现在,现代的浏览器(包括 IE 和 Microsoft Edge)使用了更先进的垃圾回收算法,已经能够正确检测和处理循环引用了。换言之,回收节点内存时,没必要非要调用 removeEventListener 了。

脱离 DOM 的引用

有时,保存 DOM 节点内部数据结构颇有用。假如你想快速更新表格的几行内容,把每一行 DOM 存成字典(JSON 键值对)或者数组颇有意义。此时,一样的 DOM 元素存在两个引用:一个在 DOM 树中,另外一个在字典中。未来你决定删除这些行时,须要把两个引用都清除.

var elements = {
    button: document.getElementById('button'),
    image: document.getElementById('image'),
    text: document.getElementById('text')
};
function doStuff() {
    image.src = 'http://some.url/image';
    button.click();
    console.log(text.innerHTML);
    // 更多逻辑
}
function removeButton() {
    // 按钮是 body 的后代元素
    document.body.removeChild(document.getElementById('button'));
    // 此时,仍旧存在一个全局的 #button 的引用
    // elements 字典。button 元素仍旧在内存中,不能被 GC 回收。
}

此外还要考虑 DOM 树内部或子节点的引用问题。假如你的 JavaScript 代码中保存了表格某一个 <td> 的引用。未来决定删除整个表格的时候,直觉认为 GC 会回收除了已保存的 <td> 之外的其它节点。实际状况并不是如此:此 <td> 是表格的子节点,子元素与父元素是引用关系。因为代码保留了 <td> 的引用,致使整个表格仍待在内存中。保存 DOM 元素引用的时候,要当心谨慎。

闭包

若是闭包的做用域中保存着一个 HTML 元素,则该元素没法被销毁。(下面代码来自高程)

闭包是 JavaScript 开发的一个关键方面:匿名函数能够访问父级做用域的变量。

function assgin() {
    var ele = document.getElementById('someEle');
    ele.onclick = function(){
        alert(ele.id);
    }
}

以上代码建立了一个做为 ele 元素事件处理程序的闭包,而这个闭包有建立了一个循环的引用,因为匿名函数保存了一个 assgin() 的活动对象的引用 ,所以没法减小对 ele 的引用次数 , 只要匿名函数存在,ele的引用次数至少是 1。咱们能够稍微改写一下:

function assgin() {
    var ele = document.getElementById('someEle');
    var id = ele.id
    ele.onclick = function(){
        alert(id);
    }
    ele = null;
}

上面代码中,经过把 ele.id 的一个副本保存在一个变量中,而且在比保重引用该变量消除了循环引用,可是这样还不能解决内存泄露,闭包会引用包含函数的整个活动对象,而其中包含着 ele ,即便闭包不直接引用 ele ,包含函数的活动对象中也会保存 一个引用,所以须要把 ele 变量设置为 null ,这样就解除了对 DOM 对象的引用,减小其引用数,确保能正常回收。

关于内存的发现 chrome 的使用~暂时没有使用过,看不太明白,就不 copy 了。

js闭包测试 => 看不懂~

上述内容 copy 自下面两者:

JavaScript 内存泄漏教程-阮一峰
4类 JavaScript 内存泄漏及如何避免

ajax 原生实现

var xhr = createXHR()
xhr.onreadystatechange = function() {
    if(xhr.readyState == 4) {
        if(xhr.status == 200) {
            console.log(xhr.responeText)
            //do sth...
        } else {
            console.log('request fail' + xhr.status)
        }
    }
};
xhr.open('get', 'hello.com', true)
xhr.send(null);

闭包的理解

闭包是指有权访问另外一个函数做用域中的变量的函数。
这个口述我仍是不知道怎么说,或许是应用不够~看了无数文章到头来敌不过忘记~也可能我理解的仍是不到位吧~我的不解释了,放参考连接吧
How do JavaScript closures work?--StackOverflow
学习Javascript闭包(Closure)--阮一峰的网络日志
JS 中的闭包是什么--方应杭
JavaScript 中 闭包 的详解
闭包--MDN
闭包的应用

[html中一段文本内容 hdslakddnska8das ,将文本中含有数组['d', 'a', '', '8'] 中的内容标记为红色文本(字符串有改动)](http://hexin.life/more/pdd.html)

设定 html 结构

<style>
        .mark {
            color: red;
        }
    </style>
    
<html>
    <body>
        <div class='textToMark'>
        hdslakddnska8das
        </div>
    </body>
</html>

方法一:循环

const textToMark = document.querySelector('.textToMark');
    
    const text = textToMark.innerHTML;

    const arr = ['d', 'a', '*', '8'];

    const newText = text.split('');

    function toMark (textArr, arr) {
        for(let i = 0; i < newText.length; i++) {
            for(let j = 0; j < arr.length; j++) {
                if(newText[i] == arr[j]) {
                    newText[i] = `<span class='mark'>${newText[i]}</span>`;
                }
            }
        }
        return newText;
    }
    toMark(newText, arr);
    textToMark.innerHTML = newText.join('');

方法二: 字符串的 replace

const textToMark = document.querySelector('.textToMark');
    
    const text = textToMark.innerHTML;

    const reg = /[da\*8]+/g;

    var newtext = text.replace(reg, (match) => {
        return match = `<span class='mark'>${match}</span>`;
    });
    
    textToMark.innerHTML = newtext;

代码为我的写出,若是有更好的办法欢迎指教

原生JS建立这样的 dom 结构 < div id='hello'> < p class='textToMark'>hdslakddnska8das< p>< /div>

function createElement() {
        var body = document.body;
    
        var fragment = document.createDocumentFragment()      

        var div = document.createElement('div')
        div.setAttribute('id', 'hello')

        fragment.appendChild(div)

        var p = document.createElement('p')
        p.className = 'textToMark'
        p.innerHTML = 'hdslakddnska8das'

        div.appendChild(p);
        body.appendChild(fragment)
    }
    createElement();

感谢评论指出,已改正,关于节点建立 createElement 的效率问题,若是当插入的节点不少的时候,createElement 的效率会不如 createDocumentFragment .
createElement 每次 append 一个节点的时候,都会致使页面的重排,例如:

数据为这样:

<ul id="myList">
    <li>
        <a href="www.baidu.com"></a>
    </li>
    <li>
        <a href="www.helloworld.com"></a>
    </li>
</ul>


var data = [
    { name: '36O秋招', url: 'http://campus.360.cn/2015/grad.html'},
    { name: 'TX校招', url: 'http://join.qq.com/index.php'}
]
function appendChildToElement(appendToElement, data) {
    var a, li;
    for (var i = 0, len = data.length; i < len; i++) {
        a = document.createElement('a');
        a.href = data[i].url;
        a.appChild(document.createTextNode(data[i].name))
        li = document.createElement('li');
        li.appendChild(a);
        appendChildToElement(li);
    }
}

这种状况下,data 内的每个对象插入到 DOM 结构的时候都会触发一次重排,所以效率会较低。
可是咱们能够改变他的 display 属性,临时从文档移除 ul ,便可有效减小重排次数。

var ul = document.getElementById('myList');
ur.style.display = 'none';
appendChildToElement(ul, data);
ul.style.display = 'block';

固然,更好的办法就是利用 createDocumentFragment 来建立一个文档片断.

var fragment = document.createElementFragment();
appendChildToElement(fragment, data);
document.getElementById('myList').appendChild(fragment);

只访问了一次 DOM 节点,只触发了一次重排;再次感谢 @xaclincoln 的指出。

查了一些关于 createDocumentFragment 和 createElement 比较的文章。

建立一个函数对 JS 基础类型 ( function, boolean, array, number, string, object) 进行值复制

function valueToCopy (valueBeCopy) {
        var copyValue;
        if (typeof (+valueBeCopy) === 'number' && typeof valueBeCopy !== 'object') {
            copyValue = +valueBeCopy;
        } else if (typeof valueBeCopy === 'string') {
            copyValue = parseInt(copyValue);
        } else if (typeof valueBeCopy === 'object'){
            if(Array.isArray(valueBeCopy)) {
                copyValue = valueBeCopy.slice();
            }
            copyValue = JSON.parse(JSON.stringify(valueBeCopy))
        } 
            copyValue = valueBeCopy;
        // console.log(copyValue)
        return copyValue;   
    }

test img

url 输入到页面完成经历了什么

感受这篇文章很是很是详细了~太长了,过段时间再整理(抄袭~)
老生常谈-从输入url到页面展现到底发生了什么

选择题

执行顺序

var input = document.getElementById('cls')

input.onmouseup = function() {
    console.log('onmouseup')
}
input.onmousedown = function() {
    console.log('onmousedown')
}
input.onclick = function() {
    console.log('onclick')
}
input.onfocus = function() {
    console.log('onfocus')
}

onmousedown => onfocus => onmouseup => onclick

a 连接默认事件的阻止

A. a.onmouseup = function(e) {

e.preventDefault()
}

B. a.onmousedown = function(e) {

e.preventDefault()
}

C. a.onclick = function(e) {

e.preventDefault()
 }

D. A B C 均可以~

  • => 经测试只有 onclick 能够

IE浏览器中 attachEvent 方式的事件绑定

attachEvent的this老是Window。

el.attachEvent('onclick', function(){
    alert(this);
});

HTTP状态码

  • 400 Bad Request
    因为明显的客户端错误(例如,格式错误的请求语法,太大的大小,无效的请求消息或欺骗性路由请求),服务器不能或不会处理该请求。[31]

  • 401 Unauthorized(RFC 7235)
    参见:HTTP基本认证、HTTP摘要认证

相似于403 Forbidden,401语义即“未认证”,即用户没有必要的凭据。[32]该状态码表示当前请求须要用户验证。

注意:当网站(一般是网站域名)禁止IP地址时,有些网站状态码显示的401,表示该特定地址被拒绝访问网站。

  • 402 Payment Required
    该状态码是为了未来可能的需求而预留的。该状态码最初的意图可能被用做某种形式的数字现金或在线支付方案的一部分,但几乎没有哪家服务商使用,并且这个状态码一般不被使用。若是特定开发人员已超过请求的每日限制,Google Developers API会使用此状态码。[34]

  • 403 Forbidden
    服务器已经理解请求,可是拒绝执行它。与401响应不一样的是,身份验证并不能提供任何帮助,并且这个请求也不该该被重复提交。若是这不是一个HEAD请求,并且服务器但愿可以讲清楚为什么请求不能被执行,那么就应该在实体内描述拒绝的缘由。固然服务器也能够返回一个404响应,假如它不但愿让客户端得到任何信息。

选择正确答案(构造函数的引用地址)

var str = 'asd;  
var str2 = new String(str)  var str1 = new String(str)
console.log(str1 == str2 , str1 === str2)

A. true true
B. true false
C. false true
D. false false

// => 输出 => false false

由于 new 出来的俩个字符串引用地址不一样

下面的输出结果 (this 指向问题)

function one () { 
        this.name = 1;
        return function two () {
                name = 2;
            return function three() {
                var name = 3;
                console.log(this.name);
            }
        }
    }
    one()()()  // => 2;

还有一部分题忘掉喽 ~ 还有一些题具体的记不太清了,稍做修改,考点计本差很少,上面答案有的是我本身写的,有的是我 google 整理出来的,笔试期间摄像头坏了,并且不当心弹出去了三四次~就当练习了吧,反正简历也没准备好呢,哦,对了,考点大多都在高程中有详细讲解,须要好好看一下高程,面试应该会问一些 Node 和 ES6吧,若是有错误或者更好的方法请告诉我

更多笔试整理更新在我的博客Github,欢迎小伙伴来一块儿准备秋招(求大腿抱)。

相关文章
相关标签/搜索