类数组

case 1

  1. function test (a, b) {
  2. arguments[2] = 3;
  3. console.log(arguments.length);
  4. console.log(arguments[2]);
  5. }
  6. test(1, 2);
  7. console.log(test.length);
  8. // 2 3 2

首先 arguments 是一个类数组,兼顾对象和数组的特性。
arguments[2] 相当于给对象赋值,并不会修改数组的 length属性。
访问 arguments[``2``] 就是获取对象属性,值为3。

case 2

const obj = {
    '2': 3,
  '3': 4,
  push: Array.prototype.push
}

obj.push(1);

console.log(obj.length);
console.log(obj);

// 1 ['0': 1, '2': 3, length: 1, push: Function]

obj对象添加 push方法后,并有了数组的特性,这时可称为类数组(当前没有 length属性)。
调用 push方法添加元素后,发现当前没有 length属性,会为对象添加 length属性,值为 1。
这时,打印对象的 length属性就为 1。

case 3

const obj = {
    '2': 3,
  '3': 4,
  length: 2,
  push: Array.prototype.push
}

obj.push(1);

console.log(obj.length);
console.log(obj);

// 3 ['2': 1, '3': 4, length: 3, push: Function]

调用 push 方法时,会添加元素到数组里。
因为数组下标 从 0 开始,当前有两个元素,所以新元素下标 为 2,执行 obj[2] = 1;
同时length + 1 为 3。

toString

case 1

({} + {}).length; 
// 30

两个对象相加取 length,要使代码有意义。这时 {} 会调用 toString 方法。
'[object Object]' + '[object Object]' ,结果为字符串 [object Object][object Object]
这时取字符串的 length 为 30。

case 2

([] + []).length;

// 0

数组相加取 length,同样调用自己的 toString方法,相加后为空串,所以 length为 0。

case 3

[1,2,3].toString();

// 1,2,3

只有这样 toString 方法才有意义。

case 4

(function () {}).length; 

// 0

打印的实际是函数形参个数。

case 5

({} + (function () {console.log(123)})()).length;

// 123 24

立即执行函数先执行,打印 123。然后会返回 undefined
{} + undefined,会调用 toString方法,即 [object Object]undefined,取其 length为 24。

case 6

var a = {
  num: 0,
  toString: function () {
    return ++this.num;
  }
}

if (a == 1 && a == 2 && a == 3) {
  console.log('you win');
}

要判断一个变量同时满足等于 1,2,3,那么必须针对 a 进行操作。
判断元素相等时,如果类型不同,会进行隐式类型转换,正好借助对象的 toString 方法。
只要保证 JS 在判断相等时,返回不同的值即可。
同理,使用 Array 也可以实现上述效果。

Array.prototype.toString = function () {
  this[0]++;
  return this[0];
}

var a = [0];

if (a == 1 && a == 2 && a == 3) {
  console.log('you win');
}

还可以使用 Object.defineProperty 实现。

let num = 0;

Object.defineProperty(window, 'a', {
  get () {
    return ++num;
  }
});

if (a == 1 && a == 2 && a == 3) {
  console.log('you win');
}

use strict

case 1

function Test () {}

Test.prototype.a = function () {
  console.log(this);
}

new Test().a.call(null);
new Test().a.call(undefined);

// window window

非严格模式下,调用 call 方法传入 nullundefined,值为 false(JS 引擎认为无意义)。所以 this指向 window

case 2

'use strict'

function Test () {}

Test.prototype.a = function () {
  console.log(this);
}

new Test().a.call(null);
new Test().a.call(undefined);

// null undefined

严格模式下,调用 call方法传入的参数,不会被更改。

case 3

function Test () {}
"use strict";

Test.prototype.a = function () {
  console.log(this);
}

new Test().a.call(null);
new Test().a.call(undefined);

// window window

use strict 必须写在文件最顶端,或者函数内部顶端。

case 4

function Test () {
  "use strict";
  Test.prototype.a = function () {
    console.log(this);
  }
}

new Test().a.call(null);
new Test().a.call(undefined);

// null undefined

use srtict 对作用域范围内所有内容都生效。

case 5

class Test {
  a () {
    console.log(this);
  }
}
new Test().a.call(null);
new Test().a.call(undefined);

// null undefined

class 关键字声明的对象,其内部默认使用严格模式。

JavaScript 不存在类机制,一切基于原型继承(构造函数)。**

prototype

case 1

Object.prototype = {
  b: 2
};

var obj = {
  a: 1
}

console.log(obj.b);

// undefined

内置原型是不允许修改(重写)的。比如 ArrayObject 等。

箭头函数

case 1

const test = () => {
  console.log(this);
}

test();

// window

箭头函数的 this 与父级作用域保持一致。

case 2

const test = () => {
  const t = () => {
    console.log(this);
  }
  t();
}

test();
new test();

// window 报错

使用箭头函数创建实例时,会报 is not a constructor
这里和箭头函数产生的原因有关。箭头函数是为了解决 JS 中 this 指向不稳定而出现的。
箭头函数内部的 this 是稳定的,与父级作用域的 this 保持一致。使用 new 关键字会改变其 this,违背设计初衷。

class

case 1

改写为 class 关键字的形式。

;(function () {
  var c = 1;

  function Test () {
    console.log(c);
  }

  Test.prototype.a = function () {
    Test.b();
  }

  Test.b = function () {
    console.log('I am a static function of Test constructor');
  }

  window.Test = Test;
})();
const Test = (() => {
  let c = 1;

  class Test {
    constructor () {
      console.log(c);
    }

    a () {
      Test.b();
    }

    static b () {
      console.log('I am a static function of Test constructor');
    }
  }

  return Test;
})();

ES3 原型属性对应 class 关键字 内部函数。
ES3 静态属性对应 class 关键字 内部静态(static)属性。

promise

case 1

pormise 产生原因:
promise 是异步程序同步化的解决方案,可以避免异步程序同步化阻塞同步代码执行。
顺便解决了回调地狱问题。promise 多用于内部拆分处理程序,不影响外部代码执行。