基础知识
文章目录
基本数据类型
String, Number, Boolean, Undefined, Null, Symbol(ES6)
- 使用typeof校验的值
typeof 的运算数未定义,返回的就是 "undefined".
运算数为数字 typeof(x) = "number"
字符串 typeof(x) = "string"
布尔值 typeof(x) = "boolean"
null typeof(x) = "object"
布尔值 typeof(Symbol) = "function"
-
数据存储结构
简单数据类型是存储在栈中的
引用数据类型
Object, Function, Array, Date, Regexp
- 使用typeof校验的值
typeof(Object) = "object"
typeof(Function) = "function"
typeof(Array) = "object"
typeof(Date) = "function"
typeof(RegExp) = "function"
对象,数组和null `typeof(x) = "object" `
-
数据存储结构
复杂数据类型跟简单数据类型的不同点就是在于, 简单数据类型的变量指向的是内存中的数据, 而复杂数据类型指向的是其在栈的地址,通过这个地址, 从而拿到堆内存中的数据, 因此, 如果将一个对象赋值给另一个对象的时候, 其实是把这个对象在栈中的地址传递给了另一个对象, 此时, 他们共享内存中的同一块空间以及空间里的数据, 如果对其中一个对象的一个属性进行修改的话, 那么因为两个对象是共享一块地址一个数据的, 因此另一个对象中的属性也会被改变. 如果对其中一个对象重新赋值的话, 那么这个对象就会指向另一块内存空间, 就不在与另一个对象共享同一块内存了
总结:
- 在堆中存储的是对象,栈存储的是对象的地址
- new关键字,会在堆中生成一个空对象
- 对象的诞生,地址也会生成
数据类型的真假值
JavaScript中只有6个假值:undefined, null, NaN, 0, '' (empty string), false
函数构造函数,如new +
和new Boolean
, true
、1
、”somestring”
、[Object]
, !![]
都是真值。
If([])
true
!! [] 或 !!"string"
=》 true
总结:
1. 最基本是null,undefined,if判断都是假;
2. 对于数值类型,0是假,其他为真;
3. 对于字符类型空字符串是假,其他为真,
4.对于方法属性,如果定义了就是真,否则就是假,其他所有都可以看做是这些的变相应用。
一些特例
NaN
NaN,即非数值(Not a Number)是一个特殊的值,这个数值表示本来要返回数值 的操作数未返回数值的情况(这样就不会抛出错误了)
可以通过+ .NaN
得到NaN的值,任何与NaN进行运算的结果均会为NaN
NaN与自身不相等(NaN不与任何值相等
alert( + .NaN); //NaN
alert(NaN+1); //NaN
alert(NaN==NaN); //false
null和undefined
相同点:
- 在 if判断语句中,值都默认为 false
- 大体上两者都是代表无,具体看差异
差异:
- null转为数字类型值为0,而undefined转为数字类型为 NaN(Not a + )
- undefined是代表调用一个值而该值却没有赋值,这时候默认则为undefined
- null是一个很特殊的对象,最为常见的一个用法就是作为参数传入(说明该参数不是对象)
- 设置为null的变量或者对象会被内存收集器回收
数据类型的判断以及比较
typeof
typeof的运算数未定义,返回的就是 "undefined".
运算数为数字 typeof(x) = "number"
字符串 typeof(x) = "string"
布尔值 typeof(x) = "boolean"
对象,数组和null typeof(x) = "obj“
instanceof
instanceof
运算符用来检测 constructor.prototype
是否存在于参数 object
的原型链上。
instanceof
的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype
。
使用 instanceof
判断一个对象是否为数组,instanceof
会判断这个对象的原型链上是否会找到对应的 Array
的原型,找到返回 true,否则返回 false。
[] instanceof Array; // true
但 instanceof
只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object
都是 true
。
[] instanceof Object; // true
Object.prototype.toString.call()
每一个继承 Object
的对象都有 toString
方法,如果 toString 方法没有重写的话,会返回 [Object type]
,其中 type
为对象的类型。但当除了 Object
类型的对象外,其他类型直接使用 toString
方法时,会直接返回都是内容的字符串,所以我们需要使用call
或者apply
方法来改变toString
方法的执行上下文。
const an = ['Hello','An'];
an.toString(); // "Hello,An"
Object.prototype.toString.call(an); // "[object Array]"
这种方法对于所有基本的数据类型都能进行判断,即使是 null
和 undefined
。
Object.prototype.toString.call('An') // "[object String]"
Object.prototype.toString.call(1) // "[object + ]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call({name: 'An'}) // "[object Object]"
Array.isArray()
功能:用来判断对象是否为数组
instanceof 与 isArray
当检测Array实例时,Array.isArray
优于 instanceof
,因为 Array.isArray
可以检测出 iframes
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]
// Correctly checking for Array
Array.isArray(arr); // true
Object.prototype.toString.call(arr); // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false
Array.isArray() 与 Object.prototype.toString.call()
Array.isArray()
是ES5新增的方法,当不存在 Array.isArray()
,可以用 Object.prototype.toString.call()
实现。
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
数据类型转换
对象到字符串
- 如果对象有toString()方法,javascript调用它。如果返回一个原始值(primitive value如:string number boolean),将这个值转换为字符串作为结果
- 如果对象没有toString()方法或者返回值不是原始值,javascript寻找对象的valueOf()方法,如果存在就调用它,返回结果是原始值则转为字符串作为结果
- 否则,javascript不能从toString()或者valueOf()获得一个原始值,此时throws a TypeError
const obj = {
id: 0,
name: '张三',
age: 12
}
const objToStr = JSON.stringify(obj)
console.log('obj:', obj)
console.log('objToStr:', objToStr)
对象到数字的转换步骤
- 如果对象有valueOf()方法并且返回元素值,javascript将返回值转换为数字作为结果
- 否则,如果对象有toString()并且返回原始值,javascript将返回结果转换为数字作为结果
- 否则,throws a TypeError
json字符串转对象
const str = '{"id":0,"name":"张三","age":12}'
const strToObj = JSON.parse(str)
console.log('str:', str)
console.log('strToObj:', strToObj)
===运算符判断相等
- 如果两个值不是相同类型,它们不相等
- 如果两个值都是null或者都是undefined,它们相等 null === undefined
- 如果两个值都是布尔类型true或者都是false,它们相等
- 如果其中有一个是NaN,它们不相等
- 如果都是数值型并且数值相等,他们相等, -0等于0
- 如果他们都是字符串并且在相同位置包含相同的16位值,他它们相等;如果在长度或者内容上不等,它们不相等;两个字符串显示结果相同但是编码不同和=都认为他们不相等
- 如果他们指向相同对象、数组、函数,它们相等;如果指向不同对象,他们不相等
==运算符判断相等
假如我们需要对比 x 和 y 是否相同,就会进行如下判断流程:
-
首先会判断两者类型是否相同。相同的话就是比大小了
-
类型不相同的话,那么就会进行类型转换
-
会先判断是否在对比 null 和 undefined,是的话就会返回 true
-
判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
1 == ‘1’
↓
1 == 1 -
判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
'1' == true ↓ '1' == 1 ↓ 1 == 1
-
判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断
'1' == { name: 'yck' } ↓ '1' == '[object Object]'
[] == ![] (true)解析
-
根据运算符优先级 ,! 的优先级是大于 == 的,所以先会执行 ![]
-
!可将变量转换成boolean类型,null、undefined、NaN以及空字符串(’’)取反都为true,其余都为false。
-
所以 ! [] 运算后的结果就是 false
-
也就是 [] == ! [] 相当于 [] == false
-
-
根据上面提到的规则(如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1),则需要把 false 转成 0
- 也就是 [] == ! [] 相当于 [] == false 相当于 [] == 0
-
根据上面提到的规则(如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较,如果对象没有valueOf()方法,则调用 toString())
-
而对于空数组,[].toString() -> ‘’ (返回的是空字符串)
-
也就是 [] == 0 相当于 ‘’ == 0
-
-
根据上面提到的规则(如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值)
-
Number(’’) -> 返回的是 0
-
相当于 0 == 0 自然就返回 true了
-
-
总结一下:
- [] == ! [] -> [] == false -> [] == 0 -> ‘’ == 0 -> 0 == 0 -> true
{} == !{} (false)也是同理的
关键在于 {}.toString() -> NaN(返回的是NaN)
根据上面的规则(如果有一个操作数是NaN,则相等操作符返回 false)
总结一下:
{} == ! {} -> {} == false -> {} == 0 -> NaN == 0 -> false
变量(函数)提升
-
函数提升优先于变量提升,函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到作用域顶部
-
存在提升,我们能在声明之前使用。let、const 因为暂时性死区的原因,不能在声明前使用
-
var 在全局作用域下声明变量会导致变量挂载在 window 上,其他两者不会
-
let 和 const 作用基本一致,但是后者声明的变量不能再次赋值
-
Javascript在执行前会对整个脚本文件的声明部分做完整分析(包括局部变量),从而确定实变量的作用域。
-
Javascript的变量的范围是根据方法块来划分的(也就是说以function的一对大括号{ }来划分)。切记,是function块,而for、while、if块并不是作用域的划分标准。
-
当全局变量跟局部变量重名时,局部变量的范围会覆盖掉全局变量的范围,当离开局部变量的范围后,又重回到全局变量的范围。(若想指定是全局变量可以使用 window.globalVariableName)
4 js中创建函数有两种方式:函数声明式和函数字面量式。只有函数声明才存在函数提升!如:
console.log(f1); // function f1() {}
console.log(f2); // undefined
function f1() {} // 函数声明
var f2 = function() {}
输出结果: function f1() {}
undefined
之所以会有以上的打印结果,是由于js中的函数提升导致代码实际上是按照以下来执行的:
function f1() {} // 函数提升,整个代码块提升到文件的最开始<br>
console.log(f1)
console.log(f2);
var f2 = function() {}
<script type="text/javascript">
var a = 100;
function test() {
alert(a);
var a = 10;
alert(a);
}
test();
alert(a);
// 答案 undefined 10 100
// 相当于
var a = 100;
function test() {
var a
alert(a);
a = 10;
alert(a);
}
test();
alert(a);
</script>
<script type="text/javascript">
while(true){
var i = 1;
break;
}
alert(i);
// 答案 1
</script>
<script type="text/javascript">
var i = 100;
function test() {
alert(i);
i = 50;
alert(i)
var i = 10;
alert(i);
}
test();
alert(i);
// 答案 undefined 50 10 100
</script>
<script type="text/javascript">
var i = 100;
function test() {
alert(window.i);
var i = 10;
alert(i);
}
test();
alert(i);
// 答案 100 10 100
</script>
var、let 和 const
var、let 和 const 区别的实现原理是什么?
变量生命周期
声明(作用域注册一个变量)、初始化(分配内存,初始化为undefined)、赋值
- var:遇到有var的作用域,在任何语句执行前都已经完成了声明和初始化,也就是变量提升而且拿到undefined的原因由来
- function: 声明、初始化、赋值一开始就全部完成,所以函数的变量提升优先级更高
- let:解析器进入一个块级作用域,发现let关键字,变量只是先完成声明,并没有到初始化那一步。此时如果在此作用域提前访问,则报错xx is not defined,这就是暂时性死区的由来。等到解析到有let那一行的时候,才会进入初始化阶段。如果let的那一行是赋值操作,则初始化和赋值同时进行
const、class都是同let一样的道理
let和const
let和const两个变量声明命令,他们都具有如下特性:
- 块局作用域;
- 不存在变量提升,一定声明后才能使用;
- 暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,不受外部变量影响;
- 在相同作用域范围内不允许重复声明;
const与let不同点在于:
- const如果声明的变量是简单的值,则不能改变变量的值,修改会报错;
- const如果声明的是复合类型的变量,则只保证变量地址不变,值可以变;