首页 文章详情

大挑战! JS前端知识闯关,你过得了几关?

前端阳光 | 193 2021-11-03 16:38 0 0 0
UniSMS (合一短信)

前言

基础知识真有趣,10个基础知识的题目,请君来战!

1. a.x = a = {n:2}

var a = {n:1};
a.x = a = {n:2};

// 求a.x
alert(a.x); 

解析

JavaScript 总是严格按照从左至右的顺序来计算表达式。

a.x 最后执行赋值的时候,保留的是对 原来a的引用, alert的时候,a是被重新赋值过后的a。


var a = {n:1}, ref = a;
a.x = a = {n:2};
console.log("a:", a); 
console.log("ref:",ref);
b03b4420a73eb46cd083457993f336dc.webpimage.png

2. 按位或取整数部分

求值:

100.8 | 0   
-101.6 | 0   
8589934592.8 | 0

解析

位运算符将数字视为32位的有符号整数,而32位整数的取值范围是 -2147483648 ~ 2147483647

超过32位有符号整数范围取值的数,进行位或操作取整都是不准确的。

 2147483647.1 | 0  //  2147483647 正确
 2147483648.1 | 0  // -2147483648 错误
-2147483648.1 | 0  // -2147483648 正确
-2147483649.1 | 0  //  2147483647 错误

同样的,我们可以用~~来取整数部分, 当然依旧存在超范围就不准确的问题。

~~ 100.8 // 100  正确
~~ 2147483649.1  // -2147483647  错误

答案

100.8 | 0    // 100
-101.6 | 0   // -101
8589934592.8 | 0  // 0

3. 神奇的eval

var x = "global";
function log1(){
    var x = "local";
    return eval("x")
}

function log2(){
    var x = "local";
    return window.eval("x")
}

function log3(){
    var x = "local";
    var fn = eval
    return fn("x")
}

function log4(){
    var x = "local";
    return (0eval)(x)
}

log1();
log2();
log3();
log4();

解析

eval函数具备访问调用它那是的整个作用域的能力。
间接调用, 例如绑定eval函数到另外一个变量名,通过变量名调用函数会使代码失去去所有局部作用域访问的能力。

(0, eval) 其也是eval的间接调用。

log1();   // local
log2();   // global
log3();   // global
log4();   // global

答案

4. delete知多少

delete null;
delete undefined;

解析

undefined在实现上是一个全局属性, 而null是一个关键字。

Oject.getOwnPropertyDescriptor(global, 'undefined')
复制代码
e5bf0f755333ecae9a34ce0372add2ff.webp截图_20212014102047.png

null在全局上却查询不到描述信息:

Object.getOwnPropertyDescriptor(global, 'null')
复制代码
3325c82122bc4f283bcf785a2511c1c1.webp截图_20212114102114.png

结果

delete null// true
delete undefined// false

5. 类型转换

function Person(name, age{
    this.name = name;
    this.age = age;
}
Person.prototype.valueOf = function () {
    return this.age;
}
Person.prototype.toString = function () {
    return this.name
}
Date.prototype.valueOf = function () {
    return this.getTime()
}
Date.prototype.toString = function () {
    return this.getTime() + ""
}

var person = new Person("tom"100);
console.log(person + 10);
console.log(`${person}`)
console.log("" + person)

var date = new Date(200100);
console.log(date + 0typeof (date + 0))
console.log(date + date.getTime());

分析

对象转换成基础数据类型

  • Symbol.toPrimitive 优先
  • 如果预期转为字符串,Object.prototype.toString
  • 如果无预期转为字符串, 先走 Oject.prototye.valueOf, 再走Object.prototype.toString, 特例是Date类型,是先toString,再valueOf

注意两点

  1. 预期转为字符串这种字样, 比如 `${person}`
  2. Date是特别的,是优先toString

结果

var person = new Person("tom"100); 
console.log(person + 10);  // 110
console.log(`${person}`)   // tom
console.log("" + person)   // 100

var date = new Date(200100);
console.log(date + 0typeof (date + 0)) 
// 9781920000000 string
console.log(date + date.getTime());
// 978192000000978192000000

6. 函数的length

function add(num1, num2, num3=1, ...args)
}
const boundAdd = add.bind(null10);
console.log(boundAdd.length)

问题解析

  1. 有默认值的参数,不计入长度
  2. 剩余参数不计入长度
  3. bind之后,减少对应的length长度

答案

console.log(boundAdd.length) // 1

7. this的指向

var value = 1;

var foo = {
  value2,
  barfunction () {
    return this.value;
  }
}

console.log(foo.bar());
console.log((foo.bar)());
console.log((foo.bar = foo.bar)());
console.log((false || foo.bar)());
console.log((foo.bar, foo.bar)());

解析

ES规范中,引用类型(Reference类型)有一个获取对应值的方法:GetValue。简单模拟 GetValue 的使用:

var foo = 1;

var fooReference = {
    base: EnvironmentRecord,
    name'foo',
    strictfalse
};

GetValue(fooReference) // 1;

GetValue 返回对象属性真正的值,但是要注意:调用 GetValue,返回的将是具体的值,而不再是一个 Reference

本题目如果调用了 GetValue, 那么其作用域就变成了全局。

(foo.bar) 并没有调用取值操作,而其余的均调用了取值操作。

更多详情:JavaScript深入之从ECMAScript规范解读this[1]

答案

console.log(foo.bar());   // 2
console.log((foo.bar)());  // 2
console.log((foo.bar = foo.bar)());  // 1
console.log((false || foo.bar)());   // 1
console.log((foo.bar, foo.bar)());   // 1

8.参数传递

function test(param1, param2, param3{
    param1 = "new param1";
    param3  = "new param3"

    console.log(param1 == arguments[0]);
    console.log(param3 == arguments[2]);
}


function test_strict(param1, param2, param3{
    "use strict"
    param1 = "new param1";
    param3  = "new param3"

    console.log(param1 == arguments[0]);
    console.log(param3 == arguments[2]);
}

test('param1''param2')
test_strict('param1''param2');

解析

非严格模式下:传入的参数,实参和 arguments 的值会共享,当没有传入时,实参与 arguments 值不会共享。

严格模式下,实参和 arguments 是不会共享的。

答案

test('param1''param2')
test_strict('param1''param2');
// true
// false
// false
// false

9. 小数相加

(0.1 + 0.2) + 0.3  === 0.1 + 0.2 + 0.3 

解析

简略的回答就是精确问题。
更多的回答 IEEE 754, 双精度浮点数(64位),使用1为符号位、11位指数位、52位尾数位来表示。

借助:http://www.binaryconvert.com/result_double.html

0.1 0-01111111011-1001100110011001100110011001100110011001100110011010
0.2 0-01111111100-1001100110011001100110011001100110011001100110011010
0.3 0-01111111101-0011001100110011001100110011001100110011001100110011

当然,计算还好好几个步骤

  • 对阶(大阶对小阶)
  • 位数运算
  • 结果规格化
  • 舍入处理
  • 移除检查 当然,本题,后面两项都不存在。具体细节,改篇文章再见!

答案

(0.1 + 0.2) + 0.3  === 0.1 + 0.2 + 0.3  // false

0.6000000000000001 === 0.6 // false

10. 自动补全

如下代码输出的值

var a = [[1,2],2,3]
console.log(a)
[0,2,3].map(v=> console.log(v*v))
console.log(a)

解析

javascript能智能的插入分号,但是有5个有问题的字符需要密切的注意:(,[, +, -, /, 其作为一行代码的开头,很可能产生意外的情况,所以,没事代码最后写个分号,保准没错。

答案

8869e8bda11b267c6b411035e7c73db5.webpimage.png

写在最后

技术交流请到 

引用

JavaScript深入之从ECMAScript规范解读this[3]
<<编写高质量JavaScript代码的68的有效方法>>

参考资料

[1]

JavaScript深入之从ECMAScript规范解读this: https://github.com/mqyqingfeng/Blog/issues/7

[2]

https://juejin.cn/pin/6994350401550024741: https://juejin.cn/pin/6994350401550024741

[3]

JavaScript深入之从ECMAScript规范解读this: https://github.com/mqyqingfeng/Blog/issues/7


good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter