Function 的形参与实参

  1. function test(a, b, length) {
  2. test.length += length;
  3. a = 100;
  4. b = 200;
  5. console.log(test.length);
  6. delete arguments.length;
  7. arguments.push = [].push;
  8. arguments.push(4);
  9. arguments.length += length;
  10. console.log(arguments.length);
  11. console.log(arguments);
  12. }
  13. test(1, 2, 3);
  14. console.log(test.length);

答案:
3 4 [4, 200, 3, length: 4] 3
分析:
test.length 是函数的 length 属性是代表这个函数有多少个形参,是只读的,修改会静默失败,不能被修改。所以 Line7 输出 3。
而形参 a, b, lenght 是与 arguments 这个实参的类数组有着映射的关系。所以当 a = 100; b = 200; 时,arguments 是被其修改。当然后也能被 delete 删除 arguments 的属性。
调用数组的 push 方法时,发现类数组没有 length 的属性,因为数组肯定有 length,但是此时的 arguments 被删除了。push 方法会尽让其可以执行,但以把 length 当为 0,进行下去。所以 arguments 的第一个元素被修改为 4,arguments 的 length 属性又被添加上去,为 1。接着 arguments.length 又被加上形参的 3 变成了 length:4。
总结

  • 函数的 length 代表形参的个数,只读属性,不会被修改
  • arguments 与 函数的形参有映射的关系,形参的值与 arguments 的修改会使其双向改变
  • arguments 的属性可以被增加、修改、删除
  • Array.prototype.push 方法会尽量执行,当发现(类)数组没有 length 时,会先补 length:0,然后再接着执行。

    switch 的一些细节

    ```javascript switch (false) { case 0: console.log(“0”); break; case 1: console.log(“1”); break; default: console.log(“Good bye”); break; }

switch (“a”) { case “a” || “b”: console.log(“a || b”); break; case “a” && “b”: console.log(“a && b”); break; default: console.log(“Good bye”); break; }

  1. **答案:**<br />"Good bye"<br /> "a || b"<br />**分析:**
  2. 1. switch 的参数与条件使用全等 === 来比较,即不会进行数据的隐式转换。所以是 "Good bye";
  3. 1. 比较前条件会先进行运算,"a" || "b""a" 就是 true,所以 "a" || "b" === "a"。然后再到 switch 的比较,得出结果是输出 "a || b"
  4. <a name="Q4HjW"></a>
  5. # 顶级对象化问题
  6. ```javascript
  7. var arr = [1, 2, 3];
  8. var newArr = Array(arr);
  9. console.log(arr === newArr);
  10. var obj = {
  11. 0: 1,
  12. 1: 2,
  13. 2: 3,
  14. };
  15. var newObj = Object(obj);
  16. console.log(obj === newObj);
  17. var arr = [1, 2, 3];
  18. var newArr = Object(arr);
  19. console.log(arr === newArr);
  20. var a = 1;
  21. var aStr = "1";
  22. var newA = Object(1);
  23. var newAA = new Number(1);
  24. var newAAA = Number(aStr);
  25. console.log(newA);
  26. console.log(newAA);
  27. console.log(newAAA);

答案:
false true true
Number{1} Number{1} 1
分析:
Array() 相当于 new Array() 的一种写法。用于定义数组(参数只有且是一个数值时,为定义该数值元素个数的稀疏数组,其他情况参数为数组的元素)。
而Object(), 也是省略了 new,Object 比普通的构造函数要更高深,是对象化、对象包装,目的是顶级对象化,即把参数转为相对应的引用类型。对于已经是引用类型的只会返回其本身。
所以对于 Number类型来说,Object() 顶级对象化与 new Number() 是没有什么差别的。
Number() 与 new Number() 是有差别的,没有 new 的是类型转换,加上 new 才是对象化。

Function 作用域链

var test = function () {};
console.log(test.__proto__ === Function.__proto__);
console.log(Object.__proto__ === Function.prototype);
console.log(test.__proto__ === Function.prototype);
console.log(Function.prototype === Function.__proto__);

答案:
true true true true
分析:
函数的 proto 就等于 函数原型属性 链就是等于原型

Object.prototype 的 proto

Object.prototype.proto === null,修改其 proto

Object.prototype.__proto__ = { a: 100 };
var obj = {};
console.log(obj.a);

答案:
报错,JS引擎禁修改Object.prototype.__proto。
image.png
不可变原型
使用 Object.defineProperty 来修改呢?

Object.defineProperty(Object, "prototype", {
  enumerable: true,
  writable: true,
  configurable: true,
});

Object.prototype.__proto__ = { a: 100 };
var obj = {};
console.log(obj.a);

答案:
报错,Object.prototype.__proto 是 Immutable prototype object 不可变原型对象,也就是不能被重定义
image.png

各数据类型相加的问题

console.log(([] + {} + function () {}).length);
console.log(([{}, 2, 3] + {}).length);
console.log((!true + !false).length);
console.log((new Function() + {}).length);

答案:

  1. 29
  2. 34
  3. undefined
  4. 41

分析:
引用类型与各数据类型相加在一起,最有意义是转为字符串,

  1. [].toString() => ‘’,lenght 为 0
    {}.toString() => ‘[object Object]’,length 为 15
    (function () {}).toString() => ‘function () {}’,lenght 为 14
    所以为 29
  2. [{}, 2 ,3].toString() => ‘[object Object],2,3’,length 为 19
    {}.toString() => ‘[object Object]’,lenght 15

所以为 34

  1. 这个是原始类型,会转为数值类型相加,
    !true => false => 0
    !false => true => 1
    0 + 1 === 1
    1.length 数值类型没有 undefined

  2. (new Function()).toString() => ‘function anonymous(\n) {\n\n}’,length 为 26 (转意字符为一个字符)
    {}.toString() => ‘[object Object]’,length 为 15
    所以为 41

    闭包问题

    是否为闭包?

    function test() {
    var a = 1;
    function b() {
     console.log(a + 1);
    }
    b();
    }
    test();
    

    答案:

    分析:
    广义闭包(非实用性):内层函数能访问到外层函数的作用域,就是闭包

    对象属性问题

    使逻辑成立

    if (a == 1 && a == 2 && a == 3) {
    if (a.a === 1 && a.a === 2 && a.a === 3) {
     console.log("You Win!!!!!!");
    }
    }
    

    答案: ```javascript // get修饰符定义对象属性访问器 const a = { _a: 0, _aa: 0, toString(){ return ++ this._a; }, get a(){ return ++ this._aa; } };

// 或用Object.defineProperty来定义 const a = { _a: 0, _aa: 0, toString(){ return ++ this._a; }, };

Object.defineProperty(a, ‘a’, { get(){ return ++a._aa; } })

<a name="tdR6o"></a>
# this 指向问题
```javascript
var a = 1;
function test() {
  console.log(this.a);

  return function () {
    console.log(this.a);

    return function () {
      console.log(this.a);
    };
  };
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };

test.call(obj1).call(obj2).call(undefined);
test.call(obj1).call(obj2).call(null);
test.call(obj1).call(obj2).call(NaN);
test.call(obj1).call(obj2).call("");

// 在严格模式下
test.call(obj1).call(obj2).call(undefined);
test.call(obj1).call(obj2).call(null);
test.call(obj1).call(obj2).call(NaN);
test.call(obj1).call(obj2).call("");

答案:

  • 2 3 1
  • 2 3 1
  • 2 3 undefined
  • 2 3 undefined

严格模式下

  • 2 3 报错
  • 2 3 报错
  • 2 3 undefined
  • 2 3 undefined

    对象创建问题

    用一用方法创建一个原型为null的对象,使这个对象有两个属性, 让一个属性不可枚举。
    答案:
    var obj = Object.create(null, {
    a: {value:1},
    b: {value:2, enumerable: false}
    })
    

    new Function 与 eval 的作用域

    ```javascript var a = 2; function test() { var a = 1; var t = new Function(“console.log(a)”); // ecma262 new Function 只能在全局运行 t(); } test();

var a = 2; function test() { var a = 1; eval(“(function() {console.log(a)})”); // 1 t(); }

test();

**答案:**<br />2 1<br />**分析:**<br />ECMA262 规定了 new Function的作用域只能在全局,而 eval 没有这一限制。
<a name="hTgTG"></a>
# label 语句
i, j 从0开始  i, j <= 2 两次循环, 打印i j<br />排除 i === 1 && j === 1 ,排除打印出 1,2<br />**答案:**
```javascript
for (var i = 0; i <= 2; i++) { 
  for (var j = 0; j <= 2; j++) {
    if (i === 1 && j === 1) continue;
    if (i === 1 && j === 2) continue;
    console.log(i, j);
  }
}

使用标签语句可以省略一些逻辑循环

对象属性访问器与设置器的兼容性

对于

var vm = new Vue({
  data() {
    return {
      a: 1,
      b: 2,
    };
  },
});

使对象像 Vue 数据响应(对象代理),要求支持 IE8
答案:
先不考虑 IE8 的话,会使用 Object.defineProperty() 来做数据劫持

function Vue(options) {
  var _data = options.data();
  var _this = this;
  for (var k in _data) {
    (function () {
      Object.defineProperty(_this, k, {
        get() {
          return _data[k];
        },
        set(newValue) {
          _data[k] = newValue;
        },
      });
    })(k);
  }
}

IE8 不支持 Proxy,Object.defineProperty 只支持 DOM 对象。
但是存在 Object 下存在非标准的方法来访问 Getter 和 Setter 的方法,Object.prototype.defineGetter() 和 Object.prototype.defineSetter()。虽然是被抛弃的方法,但兼容性目前异常的好。

function Vue(options) {
  var _data = options.data();
  var _this = this;
  for (var k in _data) {
    (function () {
      _this.__defineGetter__(k, function () {
        return _data[k];
      });
      _this.__defineSetter__(k, function (newValue) {
        _data[k] = newValue;
      });
    })(k);
  }
}

还有两个兄弟方法 Object.prototype.lookupGetter() 和 Object.prototype.lookupSetter(),这两个现在被 Object.prototype.getOwnPropertyDescriptor() 方法所替代

var obj = {
    get a(){
      return '123';
  },
  set a(newValue){
      console.log('234');
  }
}

console.log(obj.a);
console.log(obj.__lookupGetter__('a'));
/*
    function a(){
      return '123';
  }
*/
console.log(obj.__lookupSetter__('a'));
/*
    function a(newValue){
      console.log('234');
  }
*/
console.log(obj.getOwnPropertyDescriptor('a').get);
/*
    function a(){
      return '123';
  }
*/
console.log(obj.getOwnPropertyDescriptor('a').set);
/*
    function a(newValue){
      console.log('234');
  }
*/