# js不适合使用的语法
# 相等运算符 ==
==默认会进行类型转换,规则十分难记。
console.log( [] == ![] ) // true
console.log( {} == !{} ) // false
console.log( [] == [] )//false
①如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1;
②如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
[ ] == ![ ]
解题思路?
![] 会变成 false,false会变成0;也就是==右侧 是 0;
==在进行比较前,如果有一侧是数字,会将另一侧也转化为数字类型,才会进行比较。
Number([]) // 0; 先在等号左侧也是0
两侧都是0 所以是true。
[] == ![] -> [] == false -> [] == 0; ==右侧的转换
因为右侧是数字,所以左侧也要转换为数字类型,才会进行比较
Number([]) == 0 -> 0 == 0 //true
{} == !{} // false
{} == !{} --> {} == false --> {} == 0; 等号右侧侧的转化
因为双等号比较前,会先转化为相同的类型,所以左侧也要转为数字类型
Number( {} ) == 0 --> NaN == 0 //false
NaN和任何数字比都是false,所以{} == !{} // false
# Number([])与Number({})
Number()函数转换规则如下: 转换规则:
- 如果是Boolean值,true和false将分别转换为1和0。
- 如果是数字值,只是简单的传入和返回。
- 如果是null值,返回0。
- 如果是undefined,返回NaN。
- 如果是字符串,遵循下列规则:
- 如果是字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值,即“1”变成1,“123”会变成123,而“011”会变成11(前导的零被忽略了);
- 如果字符串中包含有效的浮点格式,如“1.1”,则将其转换为对应的浮点数值(同样也会忽略前导零);
- 如果字符串中包含有效的十六进制格式,例如"0xf",则将其他转换为相同大小的十进制整数值;
- 如果字符串是空的(不包含任何字符),则将其转换为0;
- 如果字符串中包含除上述格式之外的字符,则将其他转换成NaN.
- 如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN(或者仍然不匹配前五条规则),则调用的对象的toString()方法,然后再次依照前面的规则转换返回的字符串值。
Number([]) //=> 0
// [].valueOf() -> []
// [].toString() -> ''
// Number('') -> 0
Number({})// => NaN
// ({}).valueOf() -> {}
// ({}).toString() -> '[object Object]'
// Number('[object Object]') -> NaN
# 双等号问题(a==1&&a==2&&a==3)
//重写toString
let a={
x:0,
toString(){
console.log(`执行${this.x}`)
return ++this.x
}
}
//数据劫持
var i=0
Object.defineProperty(window,"a",{
get(){
return ++i;
}
})
//数组
var a=[1,2,3]
a.toString =a.shift
if(a==1&&a==2&&a==3){
console.log("成功")
}
// + 先toString方法
// + null==undefined
// + NaN!=NaN
// + 能转变成数字的如果需要可以转成数字
# with
改变{}内自由变量的查找规则,当做obj属性来查找,如果找不到匹配的obj属性,会报错!
with的本意是减少键盘输入。比如
obj.a = obj.b;
obj.c = obj.d;
//可以简写成
with(obj) {
a = b;
c = d;
}
但是,在实际运行时,解释器会首先判断obj.b和obj.d是否存在,如果不存在的话,再判断全局变量b和d是否存在。这样就导致了低效率,而且可能会导致意外。
引着作用域查找
var message = "Hello World"
var test =10000
// with语句: 可以形成自己的作用域
var obj = {name: "why", age: 18, message: "obj message"}
function foo() {
function bar() {
with(obj) {
console.log(message)
console.log(test)
console.log("------")
}
}
bar()
}
foo()
// obj message
// 10000
// ------
# eval
eval用来直接执行一个字符串。这条语句也是不应该使用的,因为它有性能和安全性的问题,并且使得代码更难阅读。
# function语句
在Javascript中定义一个函数,有两种写法:
function foo() { }
//和
var foo = function () { }
两种写法完全等价。但是在解析的时候,前一种写法会被解析器自动提升到代码的头部,因此违背了函数应该先定义后使用的要求,所以建议定义函数时,全部采用后一种写法。
# void
在大多数语言中,void都是一种类型,表示没有值。但是在Javascript中,void是一个运算符,接受一个运算数,并返回undefined。
void 0; // undefined
这个命令没什么用,而且很令人困惑,建议避免使用。
# 基本数据类型的包装对象
Javascript的基本数据类型包括字符串、数字、布尔值,它们都有对应的包装对象String、Number和Boolean。所以,有人会这样定义相关值:
new String("Hello World");
new Number(2000);
new Boolean(false);
这样写完全没有必要,而且非常费解,因此建议不要使用。new Object和new Array也不建议使用,可以用{}和[]代替。
# 自动插入行尾分号
Javascript的所有语句,都必须以分号结尾。但是,如果你忘记加分号,解释器并不报错,而是为你自动加上分号。有时候,这会导致一些难以发现的错误。
比如,下面这个函数根本无法达到预期的结果,返回值不是一个对象,而是undefined。
function(){
return
{
i=1
};
}
//原因是解释器自动在return语句后面加上了分号。
function(){
return;
{
i=1
};
}
# NaN
//NaN是一种数字,表示超出了解释器的极限。它有一些很奇怪的特性:
NaN === NaN; //false
NaN !== NaN; //true
alert( 1 + NaN ); // NaN
typeof NaN结果是number
与其设计NaN,不如解释器直接报错,反而有利于简化程序
# 严格模式及注意点
- 严格模式很好理解,是一种具有限制性的JavaScript模式,从而使代码隐式的脱离了“懒散(sloppy)模式”;
- 支持严格模式的浏览器在检测到代码中有严格模式时,会以更加严格的方式对代码进行检测和执行;
严格模式对正常的JavaScript语义进行了一些限制:
- 严格模式通过 抛出错误 来消除一些原有的 静默(silent)错误;
- 严格模式让JS引擎在执行代码时可以进行更多的优化(不需要对一些特殊的语法进行处理);
- 严格模式禁用了在ECMAScript未来版本中可能会定义的一些语法;
如何开启严格模式呢?严格模式支持粒度化的迁移:
- 可以支持在js文件中开启严格模式;
- 也支持对某一个函数开启严格模式;
- 严格模式通过在文件或者函数开头使用 use strict 来开启。
- 无法意外的创建全局变量
- 严格模式会使引起静默失败(silently fail,注:不报错也没有任何效果)的赋值操作抛出异常
- 严格模式下试图删除不可删除的属性
- 严格模式不允许函数参数有相同的名称
- 不允许0的八进制语法
- 在严格模式下,不允许使用with
在严格模式下,eval不再为上层引用变量严格模式下,this绑定不会默认转成对象- 在严格模式下, 自执行函数(默认绑定)会指向undefined,注意的是,定时器属于浏览器实现的方法,不管是不是严格模式,都指向window,除非是用箭头函数时指向上一级。
// eval函数不会向上引用变量了
var jsString = '"use strict"; var message = "Hello World"; console.log(message);'
eval(jsString)
console.log(message)
"use strict"
// 禁止意外创建全局变量
message = "Hello World"
console.log(message)
function foo() {
age = 20
}
"use strict"
// 静默错误直接报错
true.name = "abc"
NaN = 123
var obj = {}
Object.defineProperty(obj, "name", {
configurable: false,
writable: false,
value: "why"
})
console.log(obj.name)
// obj.name = "kobe"
- 定时器实现的机制可能与其他的不同,通过apply绑定到window
"use strict"
// setTimeout的this
// fn.apply(this = window)
setTimeout(function() {
console.log(this)
});
setTimeout(()=>{
console.log(this)
})
谷歌浏览器开源部分代码中显示:
