原始(Primitive)值通常叫作栈数据(一旦开了个房间、不可能在这个房间里对其进行修改)数组
原始类型存储的都是值,是没有函数能够调用的,如 undefined.toString() 会报错。通常咱们看到的 '1'.toString() 能够调用是由于实际上它已经被强制转换成了 String 类型也就是对象类型,因此能够调用 toString 函数 安全
在 JS 中,存在着如下几种原始值,分别是:markdown
number(typeof undefined === "undefined")
string(typeof '' === 'string')
boolean(typeof true === 'boolean')
null(typeof null === 'object')
undefined(typeof null === 'undefined')
symbol(typeof Symbol() === 'symbol')
bigInt(typeof 10n === 'bigint')(没有正式发布但即将被加入标准的原始类型)函数
undefined 和 null 的区别post
除了会在必要的状况下类型转换之外,原始类型还有一些坑性能
typeof null 会输出 object,这是 JS 存在的一个悠久 Bug,在 JS 的最第一版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头表明是对象,然而 null 表示为全零,因此将它错误的判断为 object
0.1 + 0.2 !== 0.3
,此处详见分析篇:为何 0.1 + 0.2 !== 0.3通常叫作堆数据,包括:ui
对象(Object)(typeof {} === 'object') 数组(Array)(typeof [] === 'object')
函数(Function)(typeof function(){} === 'function')spa
引用类型
与原始类型
的区别指针
引用值的变量名存在栈里,可是值倒是存在堆里,栈里的变量名只是个指针并指向了一个堆空间
,这个堆空间存的是一开始赋的值,当 arr1 = arr 时,实际上是把 arr1 指向了和 arr 指向的同一个堆空间,这样当改变 arr 的内容时,其实就是改变了这个堆空间的内容,天然一样指向这个堆空间的 arr1 的值也随着改变 // num的改变对num1彻底没有影响
var num = 123, num1 = num;
num = 234;
console.log(num) // 234
console.log(num1) // 123
// 只是改变了arr的值,可是arr1也跟着改变了
var arr = [1,2,3], arr1 = arr;
arr.push(4)
console.log(arr) // [1,2,3,4]
console.log(arr1) // [1,2,3,4]
复制代码
再来看个函数参数是对象的状况code
function test(person) {
person.age = 26
person = {
name: 'yyy',
age: 30
}
return person
}
const p1 = {
name: 'yck',
age: 25
}
const p2 = test(p1)
console.log(p1) // {name: "yck", age: 26}
console.log(p2) // {name: "yyy", age: 30}
复制代码
(1)上面代码中,首先函数传参是传递对象指针的副本
(2)到函数内部修改参数的属性这步,当前 p1 的值也被修改了
(3)可是当从新为 person 分配了一个对象时就出现了分歧,请看下图,因此最 后 person 拥有了一个新的地址(指针),即和 p1 没有任何关系,致使了 最终两个变量的值是不相同的
原始数据类型的值直接存放在栈中,对象为引用数据类型,它们的引用变量存储在栈中,指向于存储在堆中的实际对象。没法直接操纵堆中的数据,即没法直接操纵对象,但可经过栈中对对象的引用来操做对象,就像经过遥控机操做电视机同样,区别在于这个电视机自己并无控制按钮
为何引用值要放在堆中,而原始值要放在栈中?
(1)堆比栈大,栈比堆的运算速度快;对象是一个复杂的结构且能够自由扩展,如数组能够无限扩充、对象能够自由添加属性
(2)相对而言原始类型比较稳定且它只占据很小的内存,不将原始类型放在堆是由于是为了避免影响栈的效率,且经过引用到堆中查找实际对象是要花费时间的,而这个综合成本远大于直接从栈中取得实际值的成本,因此原始类型值直接存放在栈中
算术运算符(算术运算符的优先级是从左到右的)
+
:数学上的相加功能、拼接字符串(字符串和任何数据相加都会变成字符串)–
/*
//
/%
:f分别对应数学上的相减、相乘、相除、取余功能=
:赋值运算符,优先级最低()
:和数学上同样,加括号的部分优先级最高++
:自加 1 运算,当写在变量前时是先自加 1 再执行运算,写在变量后时是先运算再自加 1--
:用法和 ++ 同样,不过是减法操做+=
:让变量自加多少-=、/=、*-、%=
等等比较运算符
> 、< 、>= 、<= 、!= 、== 不严格等于、===严格等于
不严格等于
就是说这两个数据进行了转化后值相同则整两个数据相等;而严格等于
则是两个数据不进行数据转化也相等注:NaN 不等于任何数据包括它自己,null 和 undefined 就等于它自己
逻辑运算符
&& 的做用
:只有是 true 时才会继续日后执行,一旦第一个表达式就错了后面的第二个表达式根本不执行。若表达式的返回结果都是 true 则这里 && 的返回结果是最后一个正确的表达式的结果|| 的做用
:只要有一个表达式是 true 则结束,后面的就不走了且返回的结果是这个正确的表达式的结果,若都是 false 则返回结果就是 false注意:这里有一个缺点,当传的参数是一个布尔值且传的是 false,则 || 语句的特色就会忽略掉所传的这个参数值而去赋成默认的初始值,因此为了解决这个弊端,就须要利用 ES6 的一些知识
默认为 false 的值:undefined、null、" "、0、-0、false、NaN
// String(100000000000000000000000) -> "1e+23"
parseInt(100000000000000000000000); // 1
复制代码
isNaN(NaN) // true
isNaN('abc') // true
isNaN(123) // false
复制代码
undefined
,null
,false
,NaN
,''
,0
,-0
,其余全部值都转为 true,包括全部对象原始值 | 转换为数值 | 转换为字符串 | 转换为布尔值 |
---|---|---|---|
number | / | 0 -> "0",5 -> "5" | 除了 0、-0、NaN 都为 true |
string | " " -> 0,"1" -> 1,"a" -> NaN | / | 除了空字符串都为 true |
undefined | NaN | "undefined" | false |
null | 0 | "null" | false |
[] | 0 | " " | true |
[10,20] | NaN | "10,20" | true |
{} | NaN | "[object, Object]" | true |
{a: 1} | NaN | "[object, Object]" | true |
function() {} | NaN | "function(){}" | true |
true | 1 | "true" | true |
false | 0 | "false" | false |
Symbol | NaN | "function Symbol() { [native code] }" | true |
Symbol() | 抛错 | "Symbol()" | true |
一些常规和很是规的转换状况
// 常规
"0" == null // false
"0" == undefined // false
"0" == NaN // false
"0" == 0 // true
"0" == "" // false
false == null // false
false == undefined // false
false == NaN // false
false == {} // false
"" == null // false
"" == undefined // false
"" == NaN // false
"" == {} // false
0 == null // false
0 == undefined // false
0 == NaN // false
0 == {} // false
// 很是规
"0" == false // true
false == 0 // true
false == "" // true
false == [] // true
"" == 0 // true
"" == [] // true
0 == [] // true
复制代码
对 == 两边的值认真推敲,如下两个原则能够有效地避免出错,这时最好用 === 来避免不经意的强制类型转换
(1)若两边的值中有 true
或 false
,千万不要使用 ==
(2)若两边的值中有 []
、""
或者 0
,尽可能不要使用 ==
总结
undefined == null,结果是 true 且它俩与全部其余值比较的结果都是 false
- String == Boolean,须要两个操做数同时转为 Number
- String/Boolean == Number,须要 String/Boolean 转为 Number
- Object == Primitive,须要 Object 转为 Primitive(具体经过 valueOf 和 toString 方法)
==
:等于,===
:严格等于,Object.is()
:增强版严格等于
const a = 3;
const b = "3";
a == b; // true
a === b; // false,由于*a*,*b*的类型不同
Object.is( a, b ); //false,由于*a*,*b*的类型不同
复制代码
===
这个比较简单,只须要利用下面的规则来判断两个值是否恒等
若类型不一样,就不相等
若两个都是数值且是同一个值,那么相等 有一个是 NaN 就不相等 若两个都是字符串且每一个位置的字符都同样,那么相等;不然不相等 若两个值都是一样的 Boolean 值,那么相等 若两个值都引用同一个对象或函数,那么相等,即两个对象的物理地址也必须保持一致;不然不相等。 若两个值都是 null 或者都是 undefined,那么相等
Object.is()
其行为与 === 基本一致,不过有两处不一样:
+0 不等于 -0
NaN 等于自身
+0 === -0 //true
NaN === NaN // false
Object.is(0, -0) // false
Object.is(+0, -0) // false
Object.is(0, +0) // true
Object.is(-0, -0) // true
Object.is(NaN, 0/0) // true
Object.is(NaN, NaN) // true
Object.is('foo', 'foo'); // true
Object.is(window, window); // true
Object.is('foo', 'bar'); // false
Object.is([], []); // false
const foo = { a: 1 };
const bar = { a: 1 };
Object.is(foo, foo); // true
Object.is(foo, bar); // false
Object.is(null, null); // true
复制代码
Object.is()
在严格等于的基础上修复了一些特殊状况下的失误,具体来讲就是 +0
和 -0
,NaN
和 NaN
。
function objectIs(x, y) {
if(x === y) {
// 运行到 1/x === 1/y 时 x 和 y 都为 0,但 1/+0 = +Infinity,1/-0 = -Infinity 是不同的
return x !== 0 || y !== 0 || 1 / x === 1 / y;
} else {
// NaN === NaN 是false,在这里作个拦截,x !== x 必定是 NaN, y 同理
// 两个都是 NaN 时返回 true
return x !== x && y !== y;
}
}
复制代码