JS | 你知道的JS
修改一下,如何使下列等式成立? 🤔
function usr(name, age){
this.name = name
this.age = age
}
console.log(new usr('李4', 20) + new usr('赵2', 40))
// new usr('李4', 20) 李4赵2
console.log(new usr('赵2', 40) / new usr('李4', 20))
// 2
原始值 toPrimitive
Symbol.toPrimitive 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数
在 Symbol.toPrimitive 属性(用作函数值)的帮助下,一个对象可被转换为原始值。该函数被调用时,会被传递一个字符串参数 hint ,表示要转换到的原始值的预期类型。 hint 参数的取值是 "number"、"string" 和 "default" 中的任意一个
所以这个问题可以改成
function usr(name, age){
this.name = name
this.age = age
this[Symbol.toPrimitive] = function(hint) {
switch (hint) {
case 'number':
return this.age
default:
return this.name
}
}
}
在看个例子, 如何使该等式成立
var a;
// 修改一下
if(a === 1 && a === 2 & a === 3){
// 需要执行这里
console.log('dosomthing')
}
我们也可以这样做,当然我们也可以使用 valueOf/toString 的方式去修改
var a = {
i: 0,
[Symbol.toPrimitive]:function(hint){
return ++this.i
}
}
if(a === 1 && a === 2 & a === 3){
// 需要执行这里
console.log('dosomthing')
}
a 是什么
function foo(){
try{
var a = 50
throw ""
}catch(e){
console.log(a)
var a = 250
}
}
foo()
因为在执行函数 foo 的时候创建了一个单独的 EC 栈帧,在抛出异常的时候,栈帧内 Local 的 a 为 50.在抛出异常前, 所以 console.log 的结果是 50
break 能退出所有循环么
for(let i = 0; i< 10; i++) {
console.log(i)
for(let j = 0; j < 10; j ++) {
if(j > 2) {
break // ? 这里能直接跳出这个循环么,执行下边 sucess
}
console.log(j)
}
}
console.log('success')
// 答案是不可以
如何修改呢?先了解下标签声明
标签声明
JavaScript中的标签就是一个标识符。标签可以与变量重名而互不影响,因为它是另一种独立的语法元素(既不是变量,也不是类型),其作用是指示“标签化语句(labeled statement)”。
而它的声明也很简单:一个标识符后面跟一个冒号“:”。可以在多数语句前面加上这样的标签以使该语句被“标签化(labeled)”,
但是标签语句不能用作注释语句、模块导入, 实际上表示一个“语句/语句块”
在 JavaScript 中,标签只能被 break 语句与 continue 语句所引用。前者表明停止语句执行,并跳转到 break 所指示标签的范围之外;后者表明停止当前循环,并跳转到continue所指示标签的范围起点
// 带标签的代码块
test: {
console.log(2222)
}
// 不带标签的代码块
{
console.log(3333)
}
所以上述例子可以修改为:
out: for(let i = 0; i< 10; i++) {
console.log(i)
for(let j = 0; j < 10; j ++) {
if(j > 2) {
break out
}
console.log(j)
}
}
// fine
console.log('success')
// 或者 在外层循环在加上 break
for(let i = 0; i< 10; i++) {
console.log(i)
for(let j = 0; j < 10; j ++) {
if(j > 2) {
break
}
console.log(j)
}
break
}
// fine
console.log('success')
// 当然也可以修改为 使用 return 语句退出, 但是大多数时候,我们可能需要执行后边的 success
function test() {
for(let i = 0; i< 10; i++) {
console.log(i)
for(let j = 0; j < 10; j ++) {
if(j > 2) {
return
}
console.log(j)
}
}
console.log('success')
}
逗号“,”的二义性
逗号“,”既可以是语法分隔符,又可以是运算符。
连续运算符
const a = ('hellojs', 20)
console.log(a) // ??
逗号运算符是二元运算符,它能够先执行运算符左侧的操作数,然后再执行右侧的操作数,最后返回右侧操作数的值
所以 a 最后的结果是 20
语法分隔符
var a,b;
a = 20;
b = 30;
加号“+”的二义性
如果表达式中存在字符串,则优先按字符串连接进行运算
var l = 'hello' + 'js'
var n = '520'
var num = 1
var test_sum += 1
var a = n + num // 5201
由于加号“+”进行的是值运算,因此当对象(例如a)参与运算时将调用方法x.valueOf()来确定操作数的类型(T),并在类型T不符合要求时调用x.toString()来再次尝试 可以理解为先获取该项 T 的原始值类型,然后继续做运算,如果存在字符串,按照 如果表达式中存在字符串,则优先按字符串连接进行运算
看个例子
[]+{}
var t_a = [] + {} // ?
// 结果为 "[object Object]"
var t_b = [] + 1 // ?
// step
// ([]).valueOf() => [] // 不符合原始值的集中类型 string number boolean symbol undefined
// ([]).valueOf().toString() => ''
// t_b = '' + 1 // 那么安照提醒,如果表达式中存在字符串,则优先按字符串连接进行运算
// 所以最后结果是 '1'
{}+[]
var t = {} + [] // 0 ?why
// 分解一下, 如果讲上边的代码改动一下,结果将会不一样
// t = ({}) + [] // "[object Object]"
// 丢失的 {} 去了哪里,其实上边的代码是因为 js 解析执行的问题,一个关于; 的问题,上述代码会被当成 {};+ [] 来执行
// t = {}; + []
// t = +[] // [].valueOf().toString() => ""
// t = + "" === 0
括号“()”的二义性
下列表达式test的结果是什么?
var test = ('hellojs', 520)
// 520
// 优于括号是强制运算符,但是受到逗号运算符的影响,最终会返回最右侧的表达式的结果,所以是 520
括号“{}”的二义性
可以先看看下列这个例子
由于语句末尾的大括号的前后都可以省略 “;”(分)号,因此下面这行代码就值得回味了:
// 会得到什么
{1,2,3,4}
// 4 由于 逗号 连续运算符影响
所以关于 {} 大概可以分为 6 种。
复合语句块
// 自定义标签后表示复合语句块
customLabel: {
...
}
if(condition) {
...
}else {
...
}
解构
var test = {
type: 'a',
price: 23
}
var { type, price } = test
console.log(type, price) // 'a', 23
声明对象字面量
var book = {
name: '测试',
author: '小李'
}
函数声明
function test() { ... }
var test = function() { ... }
结构化异常
try{
...
}catch(e){
...
}
finally{
...
}
模版字符串赋值
var b = 20
var str = `this book price is ${b}`
消失的 a
a 会是什么
var x = {
a:100
}
x.a = x = 1
// x.a: undefined
注意一个概念:赋值运算符(=)”的优先级低于属性存取运算符(.) 所以改一下
var r = x = {
a:100
}
x.a = x = 1
// x.a = (x = 1)
// x 1
// x.a: undefined
// 实际上是先运行 (x = 1) ,对属性 x.a 进行了暂存,然而又对 x 进行了重写,所以,就有了消失的 a,但是我们之前缓存了这个 x。所以 r.a 得到正确的赋值
r.a = 1
关于 new 的问题
问题
function Foo() {
}
Foo.getName = function() { return 1 }
Foo.prototype.getName = function() { return 2 }
new Foo.getName(); // ?
new new Foo().getName(); // ?
解释
// 1,
// 因为 js 中 点的运算符会很高(17),而 new 在函数没有参数是,优先级是(17), 如果存在参数会变成(18), 从左自右执行
// 所以会变成 new (Foo.getName())
// 2
// 所以这一步可以变成 new (new (Foo().getName())) // 2