前言
- 编程语言的一般规律:用一定的词法和语法,表达一定语义,从而操作运行时。
1.文法
2.语义
3.运行时
程序 = 数据结构+算法。“算法“ -> 逻辑,“数据结构“ -> 存储。
3.1 数据结构
定义:计算机存储、组织数据的方式。
3.1.1 7种基本类型(值类型+引用类型 或者 原始类型+对象类型)
- 原始类型(值类型):Undefined;Null;Boolean;String;Number;Symbol
- 对象类型(引用类型):Object
3.1.1.1 Undefined、Null
JavaScript的最初版本是这样区分的:null是一个表示”无”的对象,转为数值时为0;undefined是一个表示”无”的原始值,转为数值时为NaN。
Undefined 类型表示”缺少值”,就是此处应该有一个值,但是还没有定义,它的类型只有一个值,就是 undefined。 JavaScript 的代码 undefined 是一个变量,而并非是一个关键字,这是JavaScript 语言公认的设计失误之一。为了避免无意中被篡改,建议使用void 0 来获取 undefined 值。void 运算符 对给定的表达式进行求值,然后返回 undefined。
典型用法:
(1) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(2)函数没有返回值时,默认返回undefined。
(3)变量被声明了,但没有赋值时,就等于undefined。
(4)对象没有赋值的属性,该属性的值为undefined。
补充:undefined在全局环境没法被赋值,在局部环境是可以被赋值的。
验证如下,左图表示在全局环境,右图表示在局部环境:
<img src=”https://i.loli.net/2019/04/28/5cc56b483c4db.png"" width=”350” height=”80”/>

Null 类型表示”没有对象”,即该处不应该有值”,也只有一个值,就是 null。它的语义表示空值,与 undefined 不同,null 是JavaScript 关键字,所以在任何代码中,你都可以放心用 null 关键字来获取 null 值。
典型用法:
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
3.1.1.2 Number
- JavaScript 中的 Number 类型基本符合 IEEE 754-2008 规定的双精度浮点数规则。即第一位用来表示符号,接下来的11位用来表示指数,其他的位数用来表示有效位。
正确比较浮点数的方法:
1.Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON
2.parseFloat((0.1+0.2).toFixed(10)) === 0.3
补充知识:ES6 在
Number
对象上面,新增一个极小的常量Number.EPSILON
。Number.EPSILON
实际上是 JavaScript 能够表示的最小精度。误差如果小于这个值,就可以认为已经没有意义了,即不存在误差了。
3.1.1.3 Object
为什么给对象添加的方法能用在基本类型(值类型或者原始类型)上?
原因是装箱和拆箱操作。JavaScript 中的几个基本类型,都在对象类型中有一个“亲戚”。它们是:Number;String;Boolean;Symbol。装箱:将值类型转化为对应的引用类型的操作。 拆箱:将引用类型转换为对应的值类型的操作
举栗子~
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//装箱
var s1 = "abc";
var s2 = s1.indexOf("a")
/*实现机制如下
(1)创建String类型的一个实例;
(2)在实例上调用指定的方法;
(3)销毁这个实例;
*/
var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;
//拆箱
var objNum = new Number(123);
var objStr =new String("123");
console.log( typeof objNum ); //object
console.log( typeof objStr ); //object
console.log( typeof objNum.valueOf() ); //number
console.log( typeof objStr.valueOf() ); //string
console.log( typeof objNum.toString() ); // string
console.log( typeof objStr.toString() ); // string
补充知识:
1.每一类装箱对象皆有私有的 Class 属性,这些属性可以用 Object.prototype.toString 获取。在JavaScript 中,没有任何方法可以更改私有的 Class 属性,因此 Object.prototype.toString 是可以准确识别对象对应的基本类型的方法,它比 instanceof 更加准确。
2.在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即拆箱转换)。拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。在 ES6 之后,还允许对象通过显式指定 Symbol.toPrimitive 来覆盖原有的行为。
举栗子~
1 | var symbolObject = Object(Symbol("a")); |
js中string和number类型互转换技巧(不用原生的 Number 和 parseInt)
1
2
3
4
5
6
7
8function numSwitchStr(param){
let type=typeof param;
if(type==='string'){
return typeof (param * 1);
}else if(type==='number'){
return typeof (param + '1');
}
}JavaScript:面向对象还是基于对象?
对象的特点:具有唯一标识性、有状态、有行为。
唯一标识性:通过内存地址来体现的。任何不同的 JavaScript 对象其实是互不相等的,栈内存不同,堆内存相同。
状态和行为:C++ 中称它们为“成员变量”和“成员函数”,Java 中则称它们为“属性”和“方法”,在 JavaScript 中,将状态和行为统一抽象为“属性”。考虑到 JavaScript 中将函数设计成一种特殊对象,所以 JavaScript 中的行为和状态都能用属性来抽象。
在实现了对象基本特征的基础上, JavaScript 中对象独有的特色是:对象具有高度的动态性,这是因为 JavaScript 赋予了使用者在运行时为对象添改状态和行为的能力。
为了提高抽象能力,JavaScript 的属性被设计成比别的语言更加复杂的形式,它提供了数据属性和访问器属性(getter/setter)两类。
数据属性具有四个特征:value:就是属性的值。writable:决定属性能否被赋值。enumerable:决定 for in 能否枚举该属性。configurable:决定该属性能否被删除或者改变特征值。 [Object.defineProperty 来定义属性 , Object.getOwnPropertyDescriptor 来查看属性 ]
访问器属性也具有四个特性:getter:函数或 undefined,在取属性值时被调用。setter:函数或 undefined,在设置属性值时被调用。enumerable:决定 for in 能否枚举该属性。configurable:决定该属性能否被删除或者改变特征值。
实际上 JavaScript 对象的运行时是一个“属性的集合”,属性以字符串或者 Symbol 为 key,以数据属性特征值 或者访问器属性特征值为 value。
JavaScript 语言标准也已经明确说明,JavaScript 是一门面向对象的语言,我想标准中能这样说,正是因为 JavaScript 的高度动态性的对象系统。(多态:同一接口的多种不同的实现方式。有继承才有多态。)
JavaScript:我们真的需要模拟类吗?(模拟基于类的面向对象)
前置知识:所有对象都有私有字段 [[prototype]],就是对象的原型;读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止。
补充知识:
1.ES6 以来,JavaScript 提供了一系列内置函数,以便更为直接地访问操纵原型。三个方法分别为:Object.create 根据指定的原型创建新对象,原型可以是 null;Object.getPrototypeOf 获得一个对象的原型;Object.setPrototypeOf 设置一个对象的原型。
2.在早期版本的 JavaScript 中,“类”的定义是一个私有属性 [[class]],语言标准为内置类型诸如Number、String、Date 等指定了 [[class]] 属性,以表示它们的类。语言使用者唯一可以访问 [[class]] 属性的方式是 Object.prototype.toString。
3.在 ES5 开始,[[class]] 私有属性被 Symbol.toStringTag 代替,Object.prototype.toString 的意义从命名上不再跟 class 相关。
栗子~
1
2
3
4
5
6
7
8var o = {}
console.log(o + "");//[object Object]
//Symbol.toStringTag 修改[[class]]属性
var o = { [Symbol.toStringTag]: "MyObject" }
console.log(o + "");//[object MyObject]
/*代码解释:o + ''会触发类型转换,Object转String需要进行拆箱转换,调用o.toString()得到"[object Object]" */ ES6 中加入了新特性 class,new 跟 function 搭配的怪异行为终于可以退休了(虽然运行时没有改变),在任何场景,我都推荐使用 ES6 的语法来定义类,而令 function回归原本的函数语义。
ES6 中引入了 class 关键字,并且在标准中删除了所有 [[class]] 相关的私有属性描述,类的概念正式从属性升级成语言的基础设施,从此基于类的编程方式成为了 JavaScript 的官方编程范式,因此不再需要模拟类了。
JavaScript对象:你知道全部的对象分类吗?
宿主对象(host Objects):由 JavaScript 宿主环境提供的对象,它们的行为完全由宿主环境决定。
- 所有的 BOM 和 DOM 对象都是宿主对象。
- BOM 对象 : window、location、navigator、screen、history…
- DOM 对象:Document、Body、Event、Form、Image、事件对象 event…
内置对象(Built-in Objects):由 JavaScript 语言提供的对象。
固有对象(Intrinsic Objects ):由标准规定,随着 JavaScript 运行时创建而自动创
建的对象实例。eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent
encodeURI encodeURIComponent Math JSON Reflect原生对象(Native Objects):可以由用户通过 Array、RegExp 等内置构造器或者特殊语法创建
的对象。
普通对象(Ordinary Objects):由{}语法、Object 构造器或者 class 关键字定义类创建的对象,它能够被原型继承。
3.1.2 对象的分类
3.2 算法(执行过程)
定义:一系列解决问题的明确指令。换言之,对于符合一定规范的输入,能够在有限时间内获得要求的输出。
前置知识:
JavaScript运行环境一般都由宿主环境和执行期环境共同构成。其中宿主环境是由外壳程序生成的,如Web浏览器就是一个外壳程序,它提供了 一个可控制浏览器窗口的宿主环境。执行期环境则由嵌入到外壳程序中的JavaScript引擎(或称为JavaScript解释器)生成。
宏观任务:宿主发起的任务。微观任务: JavaScript 引擎发起的任务。
当拿到一段 JavaScript 代码时,浏览器或者 Node 环境首先要做的就是:传递给 JavaScript 引擎,并且要求它去执行。