# 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()函数转换规则如下: 转换规则:

  1. 如果是Boolean值,true和false将分别转换为1和0。
  2. 如果是数字值,只是简单的传入和返回。
  3. 如果是null值,返回0。
  4. 如果是undefined,返回NaN。
  5. 如果是字符串,遵循下列规则:
    • 如果是字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值,即“1”变成1,“123”会变成123,而“011”会变成11(前导的零被忽略了);
    • 如果字符串中包含有效的浮点格式,如“1.1”,则将其转换为对应的浮点数值(同样也会忽略前导零);
    • 如果字符串中包含有效的十六进制格式,例如"0xf",则将其他转换为相同大小的十进制整数值;
    • 如果字符串是空的(不包含任何字符),则将其转换为0;
    • 如果字符串中包含除上述格式之外的字符,则将其他转换成NaN.
  6. 如果是对象,则调用对象的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 来开启。
  1. 无法意外的创建全局变量
  2. 严格模式会使引起静默失败(silently fail,注:不报错也没有任何效果)的赋值操作抛出异常
  3. 严格模式下试图删除不可删除的属性
  4. 严格模式不允许函数参数有相同的名称
  5. 不允许0的八进制语法
  6. 在严格模式下,不允许使用with
  7. 在严格模式下,eval不再为上层引用变量
  8. 严格模式下,this绑定不会默认转成对象
  9. 在严格模式下, 自执行函数(默认绑定)会指向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)
})

谷歌浏览器开源部分代码中显示:

最后更新: 4/15/2024, 7:31:02 AM