每日JavaScript - 18(作用域,NaN等题目)

NaN的介绍
NaN是什么?
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/NaN
英文Not-A-Numbe,通常都是在计算失败时,作为 Math 的某个方法的返回值出现的(例如:Math.sqrt(-1))或者尝试将一个字符串解析成数字但失败了的时候(例如:parseInt(“blabla”))。
有很古怪的特性:

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

建议比较时使用ES6的

Number.isNaN()

相比全局方法的isNaN(),更安全。
传统方法先调用Number()将非数值的值转为数值,再进行判断,而isNaN()新方法只对数值有效。

编写一个函数sum,使得下面满足

console.log(sum(2,3));   // Outputs 5
console.log(sum(2)(3));  // Outputs 5
const sum = (x) => {
  if (arguments.length == 2) {
    return arguments[0] + arguments[1]
  } else {
    return (y) => {
      return x + y
    }
  }
}

以上的函数是错误的,因为箭头函数没有自己的this和arguments(而且不能用作构造函数)
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions
还是正经的写

function sum(x) {
  if (arguments.length == 2) {
    return arguments[0] + arguments[1];
  } else {
    return function(y) { return x + y; };
  }
}

还可以

function sum(x, y) {
  if (y !== undefined) {
    return x + y;
  } else {
    return function(y) { return x + y; };
  }
}

下面输出什么

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));

这题我做错,还是分不清楚,引用和拷贝

var arr2 = arr1.reverse()

arr2其实是arr1经过reverse(reverse和push会改变原数组)后的引用(就是对arr1的引用),
所以

array 1: length=5 last=j,o,n,e,s
VM214:6 array 2: length=5 last=j,o,n,e,s
console.log(1 +  "2" + "2");
console.log(1 +  +"2" + "2");
console.log(1 +  -"1" + "2");
console.log(+"1" +  "1" + "2");
console.log( "A" - "B" + "2");
console.log( "A" - "B" + 2);

这是因为JavaScript是动态类型,所以才出现这么多bug,下面是解析。

示例1:1 +“2”+“2”输出:“122”说明:第一个操作在1 +“2”中执行。由于其中一个操作数(“2”)是一个字符串,所以JavaScript假定需要执行字符串连接,因此将1的类型转换为“1”,1 +“2”转换为“12”。然后,“12”+“2”产生“122”。

示例2:1 + +“2”+“2”输出:“32”说明:根据操作顺序,要执行的第一个操作是+“2”(第一个“2”之前的额外+被视为一个一元运算符)。因此,JavaScript将“2”的类型转换为数字,然后将一元+符号应用于它(即将其视为正数)。结果,下一个操作现在是1 + 2,当然这会产生3.但是,我们有一个数字和一个字符串之间的操作(即3和“2”),所以JavaScript再次转换数值赋给一个字符串并执行字符串连接,产生“32”。

示例3:1 + - “1”+“2”输出:“02”说明:这里的解释与前面的示例相同,只是一元运算符是 - 而不是+。因此,“1”变为1,然后在应用 - 时将其变为-1,然后将其加1到产生0,然后转换为字符串并与最终的“2”操作数连接,产生“02”。

示例4:+“1”+“1”+“2”输出:“112”说明:尽管第一个“1”操作数是基于其前面的一元+运算符的数值类型转换的,当它与第二个“1”操作数连接在一起时返回一个字符串,然后与最终的“2”操作数连接,产生字符串“112”。

示例5:“A” - “B”+“2”输出:“NaN2”说明:由于 - 运算符不能应用于字符串,并且既不能将“A”也不能将“B”转换为数值, “ - ”B“产生NaN,然后​​与字符串”2“串联产生”NaN2“。

例6:“A” - “B”+2输出:NaN说明:在前面的例子中,“A” - “B”产生NaN。但是任何运算符应用于NaN和其他数字操作数仍然会产生NaN。

如果数组列表太大,以下递归代码将导致堆栈溢出。你如何解决这个问题,仍然保留递归模式?

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);
    }
};

如下输出什么

console.log("0 || 1 = "+(0 || 1));
console.log("1 || 2 = "+(1 || 2));
console.log("0 && 1 = "+(0 && 1));
console.log("1 && 2 = "+(1 && 2));

输出

0 || 1 = 1
1 || 2 = 1
0 && 1 = 0
1 && 2 = 2

关键 ||遇到true,就输出true,两个true默认输出第一个true。 && 遇到 false,就输出false,两个true默认输出第二个true。

输出什么

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

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

console.log(a[b]);

对象强转string会转为“[object Object]”,因为JavaScript会隐式调用toString()Object.prototype.toString.call(this) 。所以b和c都会转为“[object Object]”,值为最后一个 456。又因为JavaScript在各种类型重写了toString()函数,所以下面会输出什么?

var a={},
    b=[4, 6],
    c=[3, 2],
    d= {3 : 2};

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

console.log(a[b]);

输出什么

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

var stoleSecretIdentity = hero.getSecretIdentity;

console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());

这是常见的考察执行上下文,就不多说了

var length = 10;
function fn() {
    console.log(this.length);
}

var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();
  }
};

obj.method(fn, 1);

同上题一样是考察this,上下文,第一个console.log很好理解

obj.method(fn, 1);

只是把fn传进去,真正调用的时候是

fn()

fn不属于任何人,在非严格模式下,this指向undefined,undefined会自动指向window。

arguments[0]();

第二个console.log,fn属于arguments,所以输出arguments的length

function () {
    try {
        throw new Error();
    } catch (x) {
        var x = 1, y = 2;
        console.log(x);
    }
    console.log(x);
    console.log(y);
})();

又是变量提升,被摆了一道,相当于

(function () {
    var x, y; // outer and hoisted
    try {
        throw new Error();
    } catch (x /* inner */) {
        x = 1; // inner x, not the outer one
        y = 2; // there is only one y, which is in the outer scope
        console.log(x /* inner */);
    }
    console.log(x);
    console.log(y);
})();

typeof undefined == typeof NULL

输出 true

输出什么

var b = 1;
function outer(){
       var b = 2
    function inner(){
        b++;
        var b = 3;
        console.log(b)
    }
    inner();
}
outer();

这段好气啊,

  function inner(){
       var b;
        b++;
        b = 3;
        console.log(b)
    }

b最开始是undefined,所以我认为undefined ++ ,肯定报错,结果不是,undefined ++ 是 b = NaN,b再等于3,所以输出3

以下为我阅读红宝书作用域链和变量对象的笔记:

执行上下文(执行环境)分为全局和局部,在浏览器端全局是window对象,在node全局是global对象。每个函数都有自己的执行环境,每个执行环境中都有一个与之关联的变量对象,环境中定义的所有变量和函数都保留在这个对象中。

当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问,作用域链的前端,始终是当前执行的代码所在环境的变量对象,末端是全局变量。这个作用域链会被保存在内部的[[Scope]属性中。作用域链本质是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。

你可能感兴趣的:(前端,Javascript)