JS基础和ES6相关
JS数据类型(记得介绍ES6新增symbol)
- 值类型(基本类型):String、Number、Boolean、Null、Undefined、Symbol(ES6引入的一种新的原始数据类型,表示独一无二的值)、bigint
- 引用数据类型:对象(Object)【包含普通对象- Object、数组对象(Array)、函数对象(Function)、日期对象(Date)、数字对象(Math)、正则对象(RegExp)】
JS强制类型转换
-
显式转换
通过手动进行类型转换,JavaScript提供了以下转型函数
转换为数值类型:Number(mix)、parseInt(string, radix)、parseFloat(string) 转换为字符串类型:toString(radix)、String(mix) 转换为布尔类型:Boolean(mix)
-
Number(mix),可以将任意类型的参数mix转换为数值类型,规则如下:
- 布尔值:true-1、false-0
- 数字值:返回本身
- null:返回0
- undefined:返回NaN
- 字符串:只包含数字—转换为十进制(忽略前导0)、包含有效的浮点格式—转换为浮点数值(忽略前导0)、空字符串—0、其他格式—NaN
- 对象:调用对象的valueOf(),依据前面规则转换返回值。如果转换结果为NaN,则调用对象的toString(),依照前面的规则转换返回的字符串值
-
**parseInt(string, radix)**函数,将字符串转换为整数类型的数值
- 忽略字符串前面的空格,直至找到第一个非空字符
- 如果第一个字符不是数字符号或者负号,返回NaN
- 如果第一个字符是数字,则继续解析直至字符串解析完毕或者遇到一个非数字符号为止
- 如果上步解析的结果以0开头,则将其当作八进制来解析;如果以0x开头,则将其当作十六进制来解析
- 如果指定radix参数,则以radix为基数进行解析
-
**parseFloat(string)**函数,将字符串转换为浮点数类型的数值
与parseInt基本相同,但也有点区别:字符串中第一个小数点符号是有效的,另外parseFloat会忽略所有前导0,如果字符串包含一个可解析为整数的数,则返回整数值而不是浮点数值
-
**toString(radix)**方法。除undefined和null之外的所有类型的值都具有toString()方法,其作用是返回对象的字符串表示
-
**String(mix)**函数,将任何类型的值转换为字符串,其规则为
- 如果有toString()方法,则调用该方法(不传递radix参数)并返回结果
- 如果是null,返回”null”
- 如果是undefined,返回”undefined”
-
**Boolean(mix)**函数,将任何类型的值转换为布尔值。
以下值会被转换为false:false、”"、0、NaN、null、undefined,其余任何值都会被转换为true。
-
-
隐式转换
在某些情况下,即使我们不提供显示转换,Javascript也会进行自动类型转换
-
用于检测是否为非数值的函数:isNaN(mix)
该函数会尝试将参数值用Number()进行转换,如果结果为“非数值”则返回true,否则返回false
-
递增递减操作符(包括前置和后置)、一元正负符号操作符
-
加法运算操作符
-
乘除、减号运算符、取模运算符
-
逻辑操作符(!、&&、||)
-
关系操作符(<, >, <=, >=)
-
相等操作符(==)
-
对象—原始值转换
当对象相加 obj1 + obj2
,相减 obj1 - obj2
,或者使用 alert(obj)
打印时会发生什么?在这种情况下,对象会被自动转换为原始值,然后执行操作。
- 如果Symbol.toPrimitive()方法,优先调用再返回
- 调用valueOf(),如果转换为原始类型,则返回
- 调用toString(),如果转换为原始类型,则返回
- 如果都没有返回原始类型,会报错
JS检测类型的方法(优缺点)
tyepof [value]
:检测数据类型的运算符[example] instanceof [class]
: 检测某一个实例是否属于这个类[example].constructor===[class]
:检测实例和类关系的,从而检测数据类型Object.prototype.toString.call([value])
:检测数据类型
-
typeof
定义:用来检测数据类型的运算符
语法:
tyepof [value]
返回值:
-
typeof
检测的结果首先是一个字符串; -
字符串中包含了对应的数据类型(例如:
“number”
、“string”
、“boolean”
、“undefined”
、“object”
、“function”
、“symbol”
、“bigint”
) -
优点:使用简单,基本数据类型值基本上都可以有效检测,引用数据类型值也可以检测出来
-
局限性:
NaN
/
Infinity都是数字类型的,检测结果都是
“number”,typeof null的结果是
“object”(这是浏览器的BUG
:所有的值在计算中都以二进制编码储存,浏览器中把前三位000
的当作对象,而null
的二进制前三位是000
,所以被识别为对象,但是他不是对象,他是空对象指针,是基本类型值);typeof
普通对象/数组对象/正则对象...
, 结果都是“object”
,这样就无法基于typeof
区分是普通对象
还是数组对象``...
等了
-
-
instanceof
- 定义:用来检测某个实例是否属于这个类
- 语法:实例instanceof类
- 属于返回true,否则反之
- 优点:可以弥补typeof无法细分对象类型的缺点(想检测这个值是否为数组,只需要看他是否为Array类的实例即可)
- 局限性:
- 要求检测的实例必须是对象数据类型的
- 基本数据类型的实例是无法基于它检测出来的
- 不管是数组对象韩式正则对象,都是 Object 的实例,检测结果都是 TRUE ,所以无法基于这个结果判断是否为普通对象
注意️:它本身不能完成数据类型检测,只是利用它(检测某个实例属否属于这个类的)特征来完成数据检测
-
constructor
- 定义:判断当前的实例的constructor的属性值是不是预估的类
- 语法:实例.constructor === 类
- 返回值:属于返回true,否则反之
- 优点:
实例.constructor
一般都等于类.prototype.constructor
也就是当前类本身,并且能检测基本数据类型 - 局限性:
- 不能够给当前类的原型进行重定向,会造成检测结果不准确
- 不能够给当前实例增加私有属性constructor,也会造成检测的结果不准确
- 非常容易被修改,因为JS中的construction是不被保护的,这样基于construct检测的值存在不确定性
注意:它本身不能完成数据类型检测,利用他的实例数据类型检测(不能重定向)
-
Object.prototype.toString.call()
- 定义:找到
Object.prototype
上的toString
方法,让toString
方法执行,并且基于call
让方法中的this
指向检测的数据值,这样就可以实现数据类型检测了 - 原理:
- 每一种数据类型的构造函数的原型上都有
toString
方法 - 除了
Object.prototype
上的toString
是用来返回当前实例所属类的信息(检测数据类型的),其余的都是转换为字符串的 对象实例.toString()
:toString
方法中的THIS
是对象实例,也就是检测它的数据类型,也就是THIS
是谁,就是检测谁的数据类型Object.prototype.toString.call([value])
所以我们是把toString
执行,基于call
改变this
为要检测的数据值
- 每一种数据类型的构造函数的原型上都有
- 使用方法:
- Object.prototype.toString.call(被检测的实例)
- ({}).toString.call(被检测的实例)
- 返回值:是一个字符串“[Object 当前被检测实例所属的类]”
- 优点:
- 专门用来检测数据类型的方法,基本上不存在局限性的数据类型检测方式
- 基于他可以有效的检测任何数据类型的值
- 局限性:
- 只能检测内置类,不能检测自定义类
- 只要是自定义类返回的都是‘[Object Object]’
注意:此方法是基于JS本身专门进行数据检测的,所以是目前检测数据类型比较好的方法
- 定义:找到
执行上下文&&词法作用域&&作用域链
执行上下文
-
定义
执行上下文是评估和执行JavaScript代码的环境的抽象概念,每当JavaScript代码在运行的时候,他都是在执行上下文中运行。当函数执行时候,会创建一个成为执行上下文的内部对象。一个执行上下文定义了一个函数执行时的环境,函数每次执行对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
-
执行上下文的类型
-
全局执行上下文
默认的,任何不在函数内部的代码都在全局上下文中,他会执行两件事:创建一个全局的window对象,并且设置this的值等于这个全局对象。一个程序中只会有一个全局执行上下文
-
函数执行上下文
每当一个函数被调用时,都会为该函数创建一个新的上下文。每个函数都有自己的执行上下文,不过是在函数被调用的时候创建的,函数上下文可以有任意多个。每当一个新的执行上下文被创建,他会按定义顺序执行一系列步骤
-
Eval函数执行上下文
在eval函数内部的代码也会有她属于自己的执行上下文
-
-
执行栈
是一种拥有LIFO(后进后出)数据结构的栈,被用来存储代码运行时候创建的所以执行上下文。当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。引擎会执行那些执行上下文位于栈顶的函数。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文。
-
怎么创建执行上下文
-
创建阶段
-
this值的确定,即this绑定
在全局执行上下文中,this的值指向全局对象,在函数执行上下文中,this的值取决于该函数是如何被调用的,如果他被一个引用对象调用,那么this会被设置成那个对象,否则this的值被设置为全局对象或者undefined
-
创建词法环境组建
词法环境的内部有两个组件:环境记录器和 一个外部环境的引用
- 全局环境:是没有外部环境引用的词法环境。全局环境的外部环境引用是null,他拥有内建的Object/Array等,在环境记录器内的原型函数还有任何用户定义的全局变量,并且this的值指向全局对象
- 函数环境:函数内部用户定义的变量存储在环境记录器中
-
创建变量环境组建
同样是一个词法环境,其环境记录器持有变量声明语句在执行上下文中创建的绑定关系。词法环境组件和变量环境的一个不同就是前者被用来存储函数声明和变量(
let
和const
)绑定,而后者只用来存储var
变量绑定。
-
-
词法作用域
-
[[scope]]:每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供JavaScript引擎存取,[[scope]]就是其中一个,[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合
-
作用域链
[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接.当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链
作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。
function a() {
function b () {
function c () {
}
c();
}
b()
}
a();
a defined a.[[scope]] ---> 0 : GO
a doing a.[[scope]] ---> 0 : aAO
1 : GO
b defined b.[[scope]] ---> 0 : aAO
1 : GO
b defined b.[[scope]] ---> 0 : bAO
1 : aAO
2 : GO
c defined c.[[scope]] ---> 0 : bAO
1 : aAO
2 : GO
c doing c.[[scope]] ---> 0 : cAO
1 : bAO
2 : aAO
3 : GO
JS执行步骤
-
语法分析
-
预编译
预编译发生在函数执行的前一刻
函数声明整体提升、变量 声明提升
- 创建AO对象(执行期上下文)
- 找形参和变量声明,将变量和形参作为AO属性名,值为undefined
- 将实参值和形参统一
- 在函数体里面找函数声明,值赋予函数体
function fn(a) { console.log(a); // function a() {} var a = 123; console.log(a); // 123 function a() { } console.log(a); // 123 var b = function () { } console.log(b); // function () {} function d() { } } fn(1) 创建AO对象 AO { a: undefined->1->function a() { }->123 b: undefined->function () { } d: undefined->function d(){ } }
function test(a, b) { console.log(a); // 1 c = 0; var c; a = 3; b = 2; console.log(b); // 2 function b () { }; function d () { }; console.log(b); // 2 } test(1); AO { a: undefined->1->3 b: undefined->function b () { }->2 c: undefined->0 d: undefined->function d () { }; }
GO { global: undefined -> 100 fn: function(){ } } global = 100; function fn() { console.log(global); // undefined global = 200