Function 的形参与实参
function test(a, b, length) {
test.length += length;
a = 100;
b = 200;
console.log(test.length);
delete arguments.length;
arguments.push = [].push;
arguments.push(4);
arguments.length += length;
console.log(arguments.length);
console.log(arguments);
}
test(1, 2, 3);
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; }
**答案:**<br />"Good bye"<br /> "a || b"<br />**分析:**
1. switch 的参数与条件使用全等 === 来比较,即不会进行数据的隐式转换。所以是 "Good bye";
1. 比较前条件会先进行运算,"a" || "b","a" 就是 true,所以 "a" || "b" === "a"。然后再到 switch 的比较,得出结果是输出 "a || b"
<a name="Q4HjW"></a>
# 顶级对象化问题
```javascript
var arr = [1, 2, 3];
var newArr = Array(arr);
console.log(arr === newArr);
var obj = {
0: 1,
1: 2,
2: 3,
};
var newObj = Object(obj);
console.log(obj === newObj);
var arr = [1, 2, 3];
var newArr = Object(arr);
console.log(arr === newArr);
var a = 1;
var aStr = "1";
var newA = Object(1);
var newAA = new Number(1);
var newAAA = Number(aStr);
console.log(newA);
console.log(newAA);
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。
不可变原型
使用 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 不可变原型对象,也就是不能被重定义
各数据类型相加的问题
console.log(([] + {} + function () {}).length);
console.log(([{}, 2, 3] + {}).length);
console.log((!true + !false).length);
console.log((new Function() + {}).length);
答案:
- 29
- 34
- undefined
- 41
分析:
引用类型与各数据类型相加在一起,最有意义是转为字符串,
- [].toString() => ‘’,lenght 为 0
{}.toString() => ‘[object Object]’,length 为 15
(function () {}).toString() => ‘function () {}’,lenght 为 14
所以为 29 - [{}, 2 ,3].toString() => ‘[object Object],2,3’,length 为 19
{}.toString() => ‘[object Object]’,lenght 15
所以为 34
这个是原始类型,会转为数值类型相加,
!true => false => 0
!false => true => 1
0 + 1 === 1
1.length 数值类型没有 undefined(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');
}
*/