学了这么久,还真就不太懂JavaScript,不信?

·  阅读 2945
学了这么久,还真就不太懂JavaScript,不信?

真的是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个鬼,遇到诡异现象,看协议标准!!!!!!!!!!!!!!!!!!!!!!!

Object.prototype.toString的定义:

  1. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
  2. Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
  3. Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".

Boolean.prototype的定义:

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")
复制代码

image.png

早期的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)。

关于这一点的实现,可以在这里看到:

github.com/babel/babel…

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 其实就是删除一个引用。

写在最后

不忘初衷,有所得,而不为所累,如果你觉得不错,你的一赞一评就是我前行的最大动力。

技术交流群请到 这里来。 或者添加我的微信 dirge-cloud,一起学习。

分类:
前端
收藏成功!
已添加到「」, 点击更改