JS必须知道的基础知识

基础知识

基本数据类型

String, Number, Boolean, Undefined, Null, Symbol(ES6)

  1. 使用typeof校验的值
    typeof 的运算数未定义,返回的就是 "undefined". 
    运算数为数字 typeof(x) = "number" 
    字符串 typeof(x) = "string" 
    布尔值 typeof(x) = "boolean"
    null typeof(x) = "object"
    布尔值 typeof(Symbol) = "function"
  1. 数据存储结构

    简单数据类型是存储在栈中的
    转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ksSbmHqN-1586918859454)(./imgs/数据类型-2.png)]

引用数据类型

Object, Function, Array, Date, Regexp

  1. 使用typeof校验的值
    typeof(Object) = "object" 
    typeof(Function) = "function"
    typeof(Array) = "object"
    typeof(Date) = "function"
    typeof(RegExp) = "function"       
    对象,数组和null `typeof(x) = "object" `
  1. 数据存储结构

    复杂数据类型跟简单数据类型的不同点就是在于, 简单数据类型的变量指向的是内存中的数据, 而复杂数据类型指向的是其在栈的地址,通过这个地址, 从而拿到堆内存中的数据, 因此, 如果将一个对象赋值给另一个对象的时候, 其实是把这个对象在栈中的地址传递给了另一个对象, 此时, 他们共享内存中的同一块空间以及空间里的数据, 如果对其中一个对象的一个属性进行修改的话, 那么因为两个对象是共享一块地址一个数据的, 因此另一个对象中的属性也会被改变. 如果对其中一个对象重新赋值的话, 那么这个对象就会指向另一块内存空间, 就不在与另一个对象共享同一块内存了

    总结:

    1. 在堆中存储的是对象,栈存储的是对象的地址
    2. new关键字,会在堆中生成一个空对象
    3. 对象的诞生,地址也会生成
      在这里插入图片描述

数据类型的真假值

JavaScript中只有6个假值:undefined, null, NaN, 0, '' (empty string), false

函数构造函数,如new +new Boolean, true1”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]"

这种方法对于所有基本的数据类型都能进行判断,即使是 nullundefined

    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]';
        };
    }

数据类型转换

对象到字符串

  1. 如果对象有toString()方法,javascript调用它。如果返回一个原始值(primitive value如:string number boolean),将这个值转换为字符串作为结果
  2. 如果对象没有toString()方法或者返回值不是原始值,javascript寻找对象的valueOf()方法,如果存在就调用它,返回结果是原始值则转为字符串作为结果
  3. 否则,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)

对象到数字的转换步骤

  1. 如果对象有valueOf()方法并且返回元素值,javascript将返回值转换为数字作为结果
  2. 否则,如果对象有toString()并且返回原始值,javascript将返回结果转换为数字作为结果
  3. 否则,throws a TypeError

json字符串转对象

const str = '{"id":0,"name":"张三","age":12}'
const strToObj = JSON.parse(str)
console.log('str:', str)
console.log('strToObj:', strToObj)

===运算符判断相等

  1. 如果两个值不是相同类型,它们不相等
  2. 如果两个值都是null或者都是undefined,它们相等 null === undefined
  3. 如果两个值都是布尔类型true或者都是false,它们相等
  4. 如果其中有一个是NaN,它们不相等
  5. 如果都是数值型并且数值相等,他们相等, -0等于0
  6. 如果他们都是字符串并且在相同位置包含相同的16位值,他它们相等;如果在长度或者内容上不等,它们不相等;两个字符串显示结果相同但是编码不同=都认为他们不相等
  7. 如果他们指向相同对象、数组、函数,它们相等;如果指向不同对象,他们不相等

==运算符判断相等

假如我们需要对比 x 和 y 是否相同,就会进行如下判断流程:

  1. 首先会判断两者类型是否相同。相同的话就是比大小了

  2. 类型不相同的话,那么就会进行类型转换

  3. 会先判断是否在对比 null 和 undefined,是的话就会返回 true

  4. 判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
    1 == ‘1’

    1 == 1

  5. 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断

     '1' == true
     ↓
     '1' ==  1
     ↓
     1  ==  1
    
  6. 判断其中一方是否为 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

变量(函数)提升

  1. 函数提升优先于变量提升,函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到作用域顶部

  2. 存在提升,我们能在声明之前使用。let、const 因为暂时性死区的原因,不能在声明前使用

  3. var 在全局作用域下声明变量会导致变量挂载在 window 上,其他两者不会

  4. let 和 const 作用基本一致,但是后者声明的变量不能再次赋值

  5. Javascript在执行前会对整个脚本文件的声明部分做完整分析(包括局部变量),从而确定实变量的作用域。

  6. Javascript的变量的范围是根据方法块来划分的(也就是说以function的一对大括号{ }来划分)。切记,是function块,而for、while、if块并不是作用域的划分标准。

  7. 当全局变量跟局部变量重名时,局部变量的范围会覆盖掉全局变量的范围,当离开局部变量的范围后,又重回到全局变量的范围。(若想指定是全局变量可以使用 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两个变量声明命令,他们都具有如下特性:

  1. 块局作用域;
  2. 不存在变量提升,一定声明后才能使用;
  3. 暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,不受外部变量影响;
  4. 在相同作用域范围内不允许重复声明;

const与let不同点在于:

  1. const如果声明的变量是简单的值,则不能改变变量的值,修改会报错;
  2. const如果声明的是复合类型的变量,则只保证变量地址不变,值可以变;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章