【javascript】《你不知道的javascript》中卷笔记

7种内建类型

null / undefined / object / number / string / boolean

ES6 新增了一个 symbol 类型

typeof 操作符 (7个值)

返回的也是7个值,typeof 总是会返回字符串:

typeof undefined === 'undefined'
typeof 42 === 'number'
typeof '42' === 'string'
typeof { life: 420 } === 'object'
typeof true === 'boolean'

//ES6 新增的
typeof Symbol() === 'symbol'

//但是 typeof null 的结果 是令人吃惊的
typeof null === 'object'

所以我们判断null的时候不能简单的这样判断,需要像下面这样:

const a = null
const isNull = !a && typeof(a) === 'object'

但是并不如像上面说的那样 typeof 有7个值,只有6个,除了null的都有,我们看下第七个是什么:

typeof function test(){}  === 'function'

javascript里面,变量没有类型,值才有。 变量在任何时候,持有任何值。

证明typeof返回的是一个字符串, 安全的防范机制,typeof var 无论var是否声明都不会报错,阻塞程序

typeof typeof 66 === 'string'

还有一个特殊的情况:

typeof NaN === 'number'

数组的小坑

数组也是对象,可以添加属性

const a = []
a[0] = 0
a['name'] = 'arr-name'
console.log(a) // [0, name: "arr-name"]
console.log(a.length) // 1

a['0'] = 'zero'
console.log(a) // ["zero", name: "arr-name"]
console.log(a.length) // 1

类数组的操作

比如DomList 我们需要对其做一些过滤操作什么的,但是它只是一个类数组【索引为数字,并且可以遍历】

// 在chrome控制台下可以做实验
var tempDomList = document.getElementsByTagName('ul')
// 我们是可以tempDomList[0]去获取第一个ul标签元素,但是没法使用数组的方法

我们以前是Array.prototype.METHOD.call(arr, params)…
比如想使用find方法这样使用:

const specialUl = Array.prorotyle.find.call(
	tempDomList, 
	(item)=>{item.className.includes('special-class')
})

现在可以直接使用 Array.from(类数组对象)

Array.from(temp).find((item)=>{
	item.className.includes('special-class')
})

小数的坑

因为0.1 和0.2 js里面,二进制表示也是不精确的,两个错误的值相加,肯定不会等于一个正确的值。
对所有使用IEEE754的语言的,都存在这个问题。

0.1 + 0.2 === 0.3 // false

NaN

注意: NaN === NaN 的结果是false
还有一个方法:isNaN
isNaN 的本意是判断值是否是 NaN , isNaN(NaN) 的结果是true
但是isNaN(‘foo’) 的结果也是true

这个bug在ES6 用新的方法来解决了: Number.isNaN

装箱

和 java有点类似,基础数据类型和包装类型,比如我们定义一个字符串:
var str = "kevin"
执行
str.length()
实际上js会自动封箱。

但是比如你想自己手动装箱,比如这样

const flag =  new Boolean(false)
if(!flag) {
	console.log('come here') //actualy never come here
}

开箱 或者拆箱

比如你有一个包装器对象,想取去底层的基本类型值,你可以使用valueOf方法。valueOf MDN地址在这里: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf

	const a  = new String("str")
	a == "str" // true
	a ==== "str" // false

valueOf 可以构造一个比较奇怪的例子,当然仅限于 == 运算符 :

	const a = { name: "kevin", valueOf: "$2000M" }
	a == "$2000M" // true
	a === "$2000M" // false

JSON.stringify

基础类型直接转换为字符串

JSON.stringify(null) 	// "null"
JSON.stringify(true)		// "true"
JSON.stringify(42)		// "42"
JSON.stringify("42")		// ""42""

当JSON.stringify()遇到 undefined function(){} symbol的时候 会自动忽略他们。

如果它们出现在对象里面,这些属性会自动的被忽略,请看例子:

const obj = { 
			name: undefined,
			sayHi: function(){ console.log('hello') },
			id: Symbol('Kevin')  
		}
console.log(JSON.stringify(obj)) 
// 输出的结果是 "{}"

如果它们出现在数组里面,这些值会被替换为null,请看例子:,请看例子:

const arr =["kevin", undefined, Symbol("kevin"), function(){}]
console.log(JSON.stringify(arr))
// 输出的结果是 "["kevin",null,null,null]"

有时候我们有这样的需求,需要将转换后的字符串格式化,加上换行/ 或者说只打印出想要的结果,这时候需要加上第二个参数和第三个,这个请直接移步 MDN-JSON.stringify

留意如果转换的目标有toJSON方法,那么这个方法首先会被调用,然后将该方法返回的值,作为序列化的参数。

布尔类型

被强制转换为boolean,然后结果是false 只有极少数,他们是
+0, -0, NaN
null, undefined
false
""
document.all //dom对象

参考(MDN-Boolean)[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Boolean]

强制将其他数据类型转换为布尔类型,可以使用Boolean(xx),但是太繁琐,可以使用!!双否定操作符。

String <-> Number;String和Number的转换

数字转字符串

	const a = 42
	const aStr1 = a.toString()
	const aStr2 = a + ''

字符串到数字

	const a = "42"
	const numberA = + a 

	const b = "42"
	const numberB = b - 0 
	// 因为 - 仅为数字减法定义的,所以b - 0 强制b的值转换为数字。
	// 当然用 b / 1 或者 b * 1 都是可以的。
	

对数组做一个减法操作


var a = [3]
var b = [1]

a - b // 结果是 2

两个数组因为 - 减号运算符必须转换为number,但是他们首先会被强制转换为string(使用toString()序列化),然后再强制转换为number。所以最后的结果是2。

转换为字符串的明确转换是这个

String(youVariable)

转换为字符串隐式转换是这个

yourVariable + ""

按照上面的数组来举例:

  var a = [3] 
  a.toString() // "3"
  String(a) // "3"
  a + ""  // "3"


这里有分割线,看了上面几个点,你可能以为String(a)a + "" 的结果是一样的,但是实际上内部使不同的。

a + "" 现在a值上面调用valueOf()方法 然后调用toString()方法

String(a) 是直接调用的toString()方法

证明:

const  a = {
   valueOf: function() { return 42 },
   toString: function() { return 4 }	
}

a + ""        //  "42"
String(a)     //  "4"

so…

but 这里还有一个

[] + {}  // "[object Object]"

{} + []  // 0

学不动了… 不学了!


真香分割线

比较:任何值与boolean


var a = "42"
var b = true

a == b // 肯定是false, 是的和你认为的一样 都是false

// 但是

a == false // 也是false

// 但是 + 1 
// 会进入if语句
if(a) {
	console.log("come here !")
}

首先我们来看这个a==b

如果typeOf(a)是Boolean,会返回ToNumber(a)==b的结果,对b来说也是这样,所以知道了吧?

然后我们看if语句,实际上if语句里面做了一个隐式转换。

// 隐式转换
if(a) {
  	//...
}
// 明确转换 * 1  强烈推荐~~~~ 
if(!!a) {
	//...
}
// 明确转换 * 2
if (Boolean(a)) {
	//...
}

比较:非object 与 object

如果一个 object / function / array 被与一个简单基本标量(string, number, boolean)进行比较。

ES5规范中是这样说的:

如果 Type(x)是一个String 或者Number而 Type(y)是一个Object,返回比较x == ToPrimitive(y)的结果。
如果 Type(y)是一个String 或者Number而 Type(x)是一个Object,返回比较y == ToPrimitive(x)的结果。

但是我们没有说Boolean的情况,你看我们上一点,已经说了Boolean的情况,这里就不在讨论。

所以就会有下面的结果:

	var arr = [42]
	arr == 24 // true

    var obj = { valueOf: function() { return 42; } }
    obj == 42 // true

请注意valueOf 和 toString方法。前面我们说了拆箱,就是一个基本类型值的object包装器(例如new String(‘abc’)这样的形式)被展开,其底层的基本类型值(‘abc’)被返回。这种行为与 == 算法中的ToPrimitive强制转换有关:

 var a = "abc"
 var b = Object(a)
 
 a === b // false
 a == b   // true
 // 这里a == b 是true,是因为b通过ToPrimitive强制转换为
 // 它的底层简单基本标量值 "abc", 它与a中的 "abc"是相等的。

这里a == b 是true,是因为b通过ToPrimitive强制转换为 它的底层简单基本标量值 “abc”, 它与a中的 "abc"是相等的。

但是 == 也会存在意外的情况,上面说的拆箱情况也有例外。考虑下面的代码:

  var a = null
  var b = Object(a)  // 与 Object() 相同
  a == b // false

  var c = undefined
  var d = Object(a)  // 与 Object() 相同
  c == d // false

  var e = NaN
  var f = Object(e) // 与 new Number(e) 相同
  e == f // false

重点:
值 null 和 undifiend 不能被装箱,他们没有等价的对象包装器。 所以Object(null), Object(undefined) 和 Object()一样,他们都仅仅产生一个普通对象。

NaN是可以被封箱到它等价的Number对象包装器中,但是拆箱之后NaN==NaN是false。

基于valueOf做奇怪的操作

我们已经知道了将一个对象和一个基本数据类型做 == 比较,实际上对这个object做了一次拆箱操作,然后再做比较。

所以才会有下面的这些结果:

  var a = ["42"]
  a == "42" // true

  var a = { valueOf: () => "42" }
  a == "42" // true
  

因为拆箱动作是调用了一个函数,这个函数我们可以控制,所以我们可以做到怪戾的操作,看下面的代码。

	if (a == 2 && a == 3) {
		console.log("come here")
	}

实际上是可以进入到if语句内部的,在知道valueOf之前,我是一脸懵逼的,但是我们可以用valueOf来达到这个效果。

var a = { 
	value: 1, 
	// 这里用箭头函数会报错,因为箭头函数内部this,指的是window
	valueOf: function() {
		this.value = this.value + 1;
		return this.value
	} 
}

if ( a == 2 && a == 3 ) {
	console.log('yep. come here ')
}

还有另外一个例子, 但是请永远不要在业务代码做这些骚操作:

 	Number.prototype.valueOf = function() {
 		return 3;
 	}
    new Number(2) == 3 // true

一个对象转为原始值,可以调用toString, 也可以调用valueOf方法,valueOf的优先级高于toString()

** 还有一个优先级高于上面两个方法的设定 Symbol.toPrimitive**

var a = {
	toString() {
		return 12
	}
}
a + 1 ;
var a = {
	toString() {
		return 12
	},
	valueOf() {
		return 13
	}
}
a + 1;

分割线,第五章文法的笔记较少,看了表达式的一堆概念性的东西。。

{} + [] 和 {} + [] 的区别

在前面我们说到了这两个表达式的值的结果:

  {} + [] // 0
  [] + {} // "[object Object]"

为什么会有这样的结果。
点击这个链接

浏览器环境下个定义一个有合法id的dom元素

我们可以直接通过id,来获取到这个dom元素。

posted on 2018-12-31 16:20  狂奔的冬瓜  阅读(257)  评论(0编辑  收藏  举报