Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

重学js —— 一元运算符(delete/void/typeof/+/-/~/!) #72

Open
lizhongzhen11 opened this issue Feb 11, 2020 · 0 comments
Open
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN

Comments

@lizhongzhen11
Copy link
Owner

lizhongzhen11 commented Feb 11, 2020

一元运算符

提个问题,看看下面的代码,+和~~区别是什么?

+true // 1
~~true // 1

+NaN // NaN 
~~NaN // 0

+(0) // 0
~~(0) // 0
+(-1) // -1
~~(-1) // -1

+Infinity // Infinity
~~Infinity // 0
~~(-Infinity) // 0

+null // 0
~~null // 0

+undefined // NaN
~~undefined // 0

1. delete 运算符

  • MDN
  • 任何使用 var 声明的属性不能从全局作用域或函数的作用域中删除。
  • 任何用 letconst 声明的属性不能够从它被声明的作用域中删除。
  • 不可设置的(Non-configurable)属性不能被移除。这意味着像Math, Array, Object内置对象的属性以及使用 Object.defineProperty() 方法设置为 不可设置 的属性不能被删除。
var a = 1
delete a // false

age = 21
delete age // true
  1. 定义 ref一元表达式 评估结果

  2. ReturnIfAbrupt(ref)

  3. 如果 ref 不是 Reference 类型,返回 true

  4. 如果 IsUnresolvableReference(ref) 为 true

    a. 断言:IsStrictReference(ref) 为 false

    b. 返回 true

  5. 如果 IsPropertyReference(ref) 为 true

    a. 如果 IsSuperReference(ref) 为 true,抛 ReferenceError 错误

    b. 定义 baseObj! ToObject(GetBase(ref))

    c. 定义 deleteStatus? baseObj.[[Delete]](GetReferencedName(ref))

    d. 如果 deleteStatusfalse 并且 IsStrictReference(ref) 为 true,抛 TypeError 错误

    e. 返回 deleteStatus

  6. 否则,

    a. 断言:ref 是对 环境记录 绑定的 引用

    b. 定义 bindingsGetBase(ref)

    c. 返回 ? bindings.DeleteBinding(GetReferencedName(ref))

注意:当在 严格模式代码 中使用 delete 运算符,如果其 一元表达式 是对一个变量,函数参数,或函数名的直接引用,将会抛出 语法错误。此外,如果在 严格模式代码 中使用 delete 运算符,并且被删除的属性其属性描述符中含有 { [[Configurable]]: false } ,也会抛出 类型错误

2. void 运算符

  1. 定义 expr一元表达式 评估结果
  2. 执行 ? GetValue(expr)
  3. 返回 undefined

为何要执行一次 GetValue

规范中的解释:GetValue必须被调用一次即使用不到它的结果,因为可能有明显的副作用

3. typeof 运算符

// 浏览器环境
typeof a // undefined
// 我压根没有声明a这个变量,但是typeof不会报错,只会返回undefined
  1. 定义 val一元表达式 评估结果

  2. 如果 val 类型,

    a. 如果 IsUnresolvableReference true,返回 "undefined"

  3. val 设为 ? GetValue(val) 的结果

  4. 根据下表返回一个字符串

val的类型 结果
Undefined "Undefined"
Null "object"
Boolean "boolean"
Number "number"
String "string"
Symbol "symbol"
BigInt "bigInt"
Object (does not implement [[Call]]) "object"
Object (implements [[Call]]) "function"

PS:规范关于BigInt使用typeof结果有误,不过已经修改了,待规范重新编辑,看 typeof 修订

4. 一元 + 运算符

  1. 定义 expr一元表达式 求值结果
  2. 返回 ? ToNumber(? GetValue(expr))

注意!+ 和 ~~ 本质上是不同的,虽然我经常会两个混用来快速转换,但是这两是有明显去别的!!!

注意:规范此处编写有误,不过未来会修改,见 + 运算符修订

5. 一元 - 运算符

一元 - 运算符将其操作数转换为 Number 类型,然后取反。对 +0 使用得到 -0,对 -0 使用得到 +0.

-(-0) // +0
-(+0) // -0
  1. 定义 expr一元表达式 求值结果
  2. 定义 oldValue? ToNumeric(? GetValue(expr))。 (PS:转为数值了)
  3. 定义 ToldValue 的类型
  4. 返回 !T::unaryMinus(oldValue)。(PS:也可能是 BigInt 类型的 ::unaryMinus

6. 按位非 ~ 运算符

  1. 定义 expr一元表达式 评估结果
  2. 定义 oldValue? ToNumeric(? GetValue(expr))。 (PS:转为数值了)
  3. 定义 ToldValue 的类型
  4. 返回 !T::bitwiseNOT(oldValue)。(PS:也可能是 BigInt 类型的 ::bitwiseNOT

7. 逻辑非 ! 运算符

  1. 定义 expr一元表达式 评估结果
  2. 定义 oldValue! ToBoolean(? GetValue(expr))
  3. 如果 oldValuetrue,返回 false
  4. 否则返回 true

粗略回答下开头问题

+ 运算符和 ~ 本质是不同的,它们俩内部算法就不同!

表示先对目标进行 按位非运算,再对结果进行一次 按位非,其实是连续进行了两次按位非运算!!!这涉及到二进制运算了。想了解的可以看下二进制,我目前对二进制了解也不算多,还停留在普通的二进制和十进制转换。。。

补充:为什么var, let和const声明的变量不能从全局环境中删除?为什么未声明但赋值的变量能删除?

PS:变量值是对象的话,对象内部属性不受影响

var a = 'a'
let b = 'b'
const c = 'c'
d = 'd'
const obj = {o: 'o'}
delete a // false
delete b // false
delete c // false
delete d // true
delete obj // false
delete obj.o // true
  1. 针对 var:先看 GlobalDeclarationInstantiation,这个方法内部会调用 CreateGlobalVarBinding(vn, false) 方法,其中用到 ObjRec.CreateMutableBinding(N, D) (注意:这里调用的是 [[ObjectRecord]]Object Environment Records,且 Dfalse),而通过算法内部得知 这里的 false 最终会传给 CreateMutableBinding 用来对 [[Configurable]] 赋值!
  2. 针对 let:还是在 GlobalDeclarationInstantiation 中,如果不是 const 声明,调用 envRec.CreateMutableBinding(dn, false),该算法内部又调用了 声明性环境记录CreateMutableBinding(N, D),该算法明确说了 如果 Dtrue 可以被删除,可惜,传过来的是 false!!!
  3. 针对 const:还是在 GlobalDeclarationInstantiation 中,如果是 const 声明,调用 envRec.CreateImmutableBinding(dn, true),该算法内部又调用了 声明性环境记录CreateImmutableBinding(N, S),如果 Strue,会是严格绑定,这里传入的的确是 true,所以这里是严格绑定。
  4. 针对 未声明但赋值的变量:看 重学js —— 块语句、声明和变量语句、空语句以及表达式语句 末尾,其实走的 赋值语句 的内部算法,最终会设置 [[Configurable]]: true

2020-03-14 补充typeof

// 来自高级前端面试
const name = 'lizz';
function fn() {
  console.log(typeof name);
  const name = '';
}
fn()

这道题结果是 引用错误,我想差了,选择了 undefined,有点丢脸。我知道 const 会形成暂时性死区,但是我误以为函数 fn 里面的 nameIsUnresolvableReference,其实不是的,我搞混了!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN
Projects
None yet
Development

No branches or pull requests

1 participant