【面试笔记】js面试25题笔记

 

自测连接>> 在js25题js21题或者js85题测验你的知识掌握。javascript

js25题笔记html

1. 使用typeof bar === "object" 可能遇到的陷阱和解决方法java

在 JavaScript 里使用 typeof 来判断数据类型,只能区分基本类型,即 “number”,”string”,”undefined”,”boolean”,”object” 五种。jquery

陷阱:若是bar是null,js会视之为object类型。数组

解决:判断bar是object但非null: 安全

console.log((bar !== null) && (typeof bar === "object"));

扩展:判断bar是数组:  typeof只能区分基本类型,它将function,array,object都视为object类型,因此要区分这三类对象就不能用typeof方法了,能够用toString.call()的返回值来判断:闭包

console.log((bar !== null) && (typeof bar === "object") && (! $.isArray(bar)));

或者用jq的isArray() isFunction()来判断:app

onsole.log((bar !== null) && (typeof bar === "object") && (toString.call(bar) !== "[object Array]"));

ECMA 5.1 中关于Object.prototype.toString.call() 的描述:函数

When the toString method is called, the following steps are taken:this

If the this value is undefined, return “[object Undefined]“.

If the this value is null, return “[object Null]“.

Let O be the result of calling ToObject passing the this value as the argument.

Let class be the value of the [[Class]] internal property of O.

Return the String value that is the result of concatenating the three Strings “[object ", class, and "]“.

对全部值类型应用 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]

2.不要写容易引发误会的代码

(function(){
  var a = b = 3;
})();

console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));

陷阱: 误觉得函数内至关于

var b = 3;
var a = b;

事实上是

b = 3;
var a = b;

所以若是不是在strict mode下,b被当成全局变量。因此应该是输出b有定义,a无定义。

3.在内联函数中,ECMA5以前this指向window,而在ECMA5 内联函数的this变成undefined。

4.闭包的应用

闭包的例子:

function f1(){
   var privateA = 1;   
   function f2(){//至关于getter方法
       return privateA;
   };
   f3 = function(n){//至关于setter方法,注意,这里的f3是一个全局变量
       privateA = n;
   };
   return f2;
}
var f4 = f1();
console.log(f4());//1
f3(2);
console.log(f4());//2

闭包的特色和做用:

1.保护函数内的变量安全,限定访问接口,实现JS私有属性和私有方法。f1的变量只有f2能访问,f2至关于f1私有变量的getter方法。

2.在内存中维持一个变量。f4持有f2,f2引用着f1,所以f4存在的时候,f2和f1也会驻留在内存中(GC不会回收,所以使用闭包时也要留意内存泄露的问题)

5.jQ为了处理多库共存,使用noConflict()来转移$使用权,而当即调用表达式能够在加载时就初始化,造成一个单例模式的效果,故而使用当即调用表达式能够解决js变量污染问题。若是jq的$使用权转移以后还想用jq,结合当即调用表达式的做用,下面这个方法使咱们仍然能使用jq:

(function($) { /* jQuery plugin code referencing $ */ } )(jQuery);

示例:

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>

<div id="foo">
    
</div>
</body>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">
    $.noConflict();
    ;(function  ($) {
        $("#foo").html("我仍是能使用jq!")
    })(jQuery);
    $("#foo").html("这里就用不了jq了!")//报错,$找不到
</script>
</html>

6.use strict 使用严格模式有什么好处

7.return的陷阱

当return单独处于一行时,javascript会自动补全为return;

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<script type="text/javascript">
   function foo1()
{
  return {
      bar: "hello"
  };
}

function foo2()
{
  return 
  {
      bar: "hello"
  };
}
console.log("foo1 returns:");
console.log(foo1());//Object {bar: "hello"}
console.log("foo2 returns:");
console.log(foo2()) //!!foo2的返回值为 undefined
</script>
</body>
</html>

8.NaN的陷阱

NaN表示一个非数字类型的值。与任何值比较都返回false,包括它本身。用数学函数操做都会返回NaN。

var a = NaN;
console.log(a);//NaN
console.log(a===a);//false
console.log(a>0);//false
console.log(Math.max(a));//NaN

可是使用typeof比较时却会判断它为数值类型。

console.log(typeof NaN === "number");  // "true"

判断NaN的方法能够用isNaN(),虽然这个方法存在不足, 有一个更好的方法是使用Number.isNaN()

Number.isNaN(NaN);        // true
Number.isNaN(Number.NaN); // true
Number.isNaN(0 / 0)       // true

// 使用全局的isNaN(),下面会返回true
Number.isNaN("NaN");      // false
Number.isNaN(undefined);  // false
Number.isNaN({});         // false
Number.isNaN("blabla");   // false
isNaN("NaN");      // true
isNaN(undefined);  // true
isNaN({});         // true
isNaN("blabla");   // true

// 使用Number.isNaN()和全局isNaN()都返回false
Number.isNaN(true);
Number.isNaN(null);
Number.isNaN(37);
Number.isNaN("37");
Number.isNaN("37.37");
Number.isNaN("");
Number.isNaN(" ");

9.for循环中变量做用域的问题

for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', (function(i) {
    return function() { console.log(i); };
  })(i));
  document.body.appendChild(btn);
}

学ECMAScript 6的时候遇过这个问题,点击任意按钮都是输出5,由于在onclick触发前,for循环已经结束了,点击按钮时输出的i是for循环结束后的i,因此都是5.为了输出0,1,2,3这样的值,咱们能够从i的做做用域和onclick的执行时机下手。如下有三个方法,其中前两个方法是基于i的做用域处理,后一个则是限制onclick的执行时机(用了当即调用方式) 

for (let i = 0; i < 5; i++) { //let让i的做用域在每次执行循环时都独立出来 
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', (function(i) {
    return function() { console.log(i); };
  })(i));
  document.body.appendChild(btn);
}
['a', 'b', 'c', 'd', 'e'].forEach(function (value, i) { //['a', 'b', 'c', 'd', 'e']一样是将每次循环的i分隔开来
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', function() { console.log(i); });
  document.body.appendChild(btn);
});
for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  (function (i) {
    btn.addEventListener('click', function() { console.log(i); });
  })(i);//当即调用表达式使btn.addEventListener在循环中完成初始化并加载
  document.body.appendChild(btn);
}

10.reverse,push,concat的使用

var arr1 = "john".split('');
var arr2 = arr1.reverse();
var arr3 = "jones".split('');
arr2.push(arr3);
console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));
//输出:
//"array 1: length=5 last=j,o,n,e,s"
//"array 2: length=5 last=j,o,n,e,s"

经过上面这段代码来巩固reverse,push和concat的使用。

首先,reverse会改变数组自己(arr1)而且返回一个数组引用(不是返回数组arr1,是返回arr1的引用),所以arr2 = arr1.reverse() 使得arr2指向arr1,从而对arr2的操做也会映射到arr1上。

此时arr1与arr2指向同一个对象,值为[ 'n' , 'h' , 'o' , 'j' ]。

其次,push在插入一个数组时,会把数组当成一个元素插进去,并且直接改变数组。所以arr2.push(arr3)的结果是[ 'n' , 'h' , 'o' , 'j' ,['j' ,'o', 'n' ,'e' ,'s']]。从中咱们知道push和concat的区别就是concat是将数组中的元素一个一个拼接到前一个数组中,并且不直接改变对象,要将拼接后的对象从新返回给原对象(使用时 arr = arr.concat(xxx);  <== > arr.push(xxx);)。而push是把元素整个放入对象结尾,因此遇到数组时push是往数组中放入array对象,concat是拼接数组。

所以在上面这段代码中,输出arr1最后一个元素时便输出了arr3这个数组(push时arr3被看成最后一个元素加入arr1指向的数组对象),而arr1和arr2指向的对象相同,因此输出二者内容都是同样的。

 11.利用事件队列解决堆栈溢出

下面这份代码在list特别大的时候会致使堆栈溢出

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        nextListItem();
    }
};

 能够巧妙地用事件队列解决这个问题

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }
};

经过上面的方法,用事件队列处理递归,而不是调用堆栈。当运行nextListItem时,若是item不为空(未到列表最后一个),这个计时方法(nextListItem)会被加入事件队列,同时该方法也就退出了调用堆栈,这样nextListItem方法会被一直加入到事件队列,知道item为空时开始逐个执行事件队列里的方法。与此同时,调用堆栈一直为空。

12. || 与 &&

0 || 1 = 1  //0为false,继续计算下一个操做数1,最终获得1
1 || 2 = 1  //1为true,忽略下一个操做数2,最终获得1
0 && 1 = 0 //0为false,忽略下一个操做数1,最终获得0
1 && 2 = 2 //1为true,继续计算下一个操做数1,最终获得2

13. == 与 ===

==判断值相等,===判断值与类型均相同。

14.js在设置对象属性时,会自动吧参数字符串化。

var a={},
    b={key:'b'},
    c={key:'c'};

a[b]=123;
a[c]=456;

console.log(a[b]); //456

缘由:b和c都是object,放入a时字符串化获得[object object] 。所以a[b],a[c]其实都是a["[object object]"],输出的值天然就是最后赋值的那一个了。

15.bind的用法(IE678不支持)

var hero = {
    _name: 'John Doe',
    getSecretIdentity: function (){
        return this._name;
    }
};

var stoleSecretIdentity = hero.getSecretIdentity;//undefined

console.log(stoleSecretIdentity());//John Doe

var stoleSecretIdentity2 = hero.getSecretIdentity.bind(hero);//John Doe
相关文章
相关标签/搜索