真的是object?
废话不说,看代码:
typeof Boolean.prototype Object.prototype.toString.call() 复制代码
3
2
1
typeof Boolean.prototype // 'object' Object.prototype.toString.call(Boolean.prototype) // '[object Boolean]' 复制代码
What? What个鬼,遇到诡异现象,看协议标准!!!!!!!!!!!!!!!!!!!!!!!
- Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
- Else if
O has a [[BooleanData]] internal slot
, let builtinTag be "Boolean". - Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
The Boolean prototype object:
- is %Boolean.prototype%.
- is an ordinary object.
- is itself a Boolean object;
it has a [[BooleanData]] internal slot with the value false
.
has a [[Prototype]] internal slot whose value is %Object.prototype%.
这里有个 [[BooleanData]] internal slot
, 啥玩意,简单理解就是内置的属性,通常是伴随对象创建产生的,开发人员是不可见的。
参见协议: Object Internal Methods and Internal Slots, 翻译一段核心:
内部槽对应于与对象关联的内部状态,并由各种 ECMAScript 规范算法使用。内部槽不是对象属性,也不是继承的。根据特定的内部槽规范,这种状态可能由任何 ECMAScript 语言类型的值或特定的 ECMAScript 规范类型值组成。除非另有明确指定,否则内部槽是作为创建对象过程的一部分分配的,不能动态添加到对象中。除非另有说明,否则内部槽的初始值是未定义的值。本规范中的各种算法创建具有内部槽的对象。但是,ECMAScript 语言没有提供将内部插槽与对象关联的直接方法。
那么,以此类推:
- Number.prototype
- String.prototype
立即执行?
先看下面四个语句吧,各自返回什么值,
// 编号1 function f(){ return 1}() // 编号2 (function f(){ return 2}()) // 编号3 var x = function f(){ return 3}() // 编号4 x = function f(){ return 4}() 复制代码
答案:
// 编号1 function f(){ return 1}() // Uncaught SyntaxError: Unexpected token ')' // 编号2 (function f(){ return 2}()) // 2 // 编号3 var x = function f(){ return 3}() // undfined // 编号4 x = function f(){ return 4}() // 4 复制代码
对于1:
是 函数申明 + 分组运算符, 分组运算符里面必须有表达式,因为上面没有,故报错。
稍微做修改
function f(){return this} (1) // 1 function f(){return this}; (1) // 1 复制代码
对于2:
()
分组运算符,因其要求里面表达式和子表达式, 它强制将function f...
作为一个表达式来做语法解析,从而避免了它被(优先地)解释为函数声明语句。
对于3: 是赋值运算,类似2, 会把function f...
作为表达式来解析。
因为其是初始化,不是赋值,并无返回值
对于4:
类似3,区别与这是赋值,有返回值。
null 和 undefined
undefined是全局属性,null是关键字。
delete null delete undefined 复制代码
Object.getOwnPropertyDescriptor(window, "null") Object.getOwnPropertyDescriptor(window, "undefined") 复制代码
早期的JS,undefined
不是一个保留的关键字,后来嘛,为了兼容,所以就老实的呆在全局变量去了。
let你怎么呢?
for (let x in {a:1}) { var x = 100 } // Uncaught SyntaxError: Identifier 'x' has already been declared for (let x in {a:1}) { let x = 100 } 复制代码
为什么第一段代码会抛出异常,而第二段没有呢? let你怎么呢?
先拆分一下代码:
- forHead: for (let x in {a:1})
- forBody: {}部分
引用大佬周爱民的原话:
语法parser引擎自己会处理这个重复检测(尽管ECMAScript没有定义)。
parser过程会维护当前块的词法上下文,并且拒绝在forBody和forHead中出现这种重复声明。而且有趣的是,这个检测过程对于let/const,以及var来说是不同的。——具体来说,let/const是只检测当前词法作用域,而var是检测词法作用域栈(scopeStack, scope chains)。
关于这一点的实现,可以在这里看到:
x = x 怎么解读
11.13.1 Simple Assignment ( = ) 62页
根据协议,可以理解为
v = GetValue(v) 复制代码
将右手端 v 的值,赋给左手端的 v 的引用。
v在作为左手端的时候,它是引用;而作为右手端的时候,它是值。
当然还有很多其他的赋值操作,例子:
- *=
- /=
- %=
- +=
- -=
- <<=
- >>=
- >>>=
- &=
- ^=
- |=
间接调用
直接放到浏览器控制塔执行,看结果:
var obj = { fn() { return this === obj } }; obj.fn(); // true (obj.fn)(); // true (0, obj.fn)() // false 复制代码
这里涉及两个运算符, 一个()
分组运算符,一个,
号运算符。
()
不产生赋值行为, 故(obj.fn)()
等同于obj.fn()
,
产生赋值行为,故(0, obj.fn)()
等同于(x = (0, obj.fn))()
, this上下文发生改变。
delete为什么不抛出异常
当你尝试删除一个不存在的变量的时候,并不会抛出异常。而且其还会返回一个布尔值true。
delete xxxxxxxxx // true delete 10 // true 复制代码
你删除一个不存在的东西,咋可能为true呢?
这个嘛,早期javascript并没有try/catch的语法来捕获错误,javaScript 就返回true,表示删除没有异常。
- delete 10 中 这个10 是一个表达式的值,其实并没有操作,返回true,用于表示没有错误。
- delete x 其实就是删除一个引用。
写在最后
不忘初衷,有所得,而不为所累,如果你觉得不错,你的一赞一评就是我前行的最大动力。