js知识点梳理

322 阅读21分钟

概述

更详细可参考这篇

数据类型

在js中一共有八种数据类型,分别为七种primary type

  • number
  • string
  • boolean
  • null
  • undefined
  • symbol
  • bigint

和object。

primary types

number

js的number属于双精度IEEE754 64位浮点类型。 其中 符号位S:1位,表示正负 阶码E:11位,表示小数点偏移的位数,实际值等于阶码-偏移值,偏移值为1023 尾数M:52位,表示小数部分,最高位默认为1,因此不包含在64位中

整体的值为 (-1)**S*(1.M)*2**(E-1023)

表示的整数范围,即精度,要看尾数,即最大为尾数全为1,即范围是-(2**53-1)到2**53-1 这被称为安全整数,两端值分别为Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER

能表示的数字范围,要看阶码,阶码不能全为0或全为1,即[1,2**11-2],减去偏移值的范围为[−1022, 1023],而尾数的范围是[1,2),因此最大值为2^1023*2,此时尾数为2,阶码是1023,即Number.MAX_VALUE,超过就是infinite

最小值,2**(-1022),此时尾数为1,阶码是-1022,因为这时候是以尾数最高位默认为1为前提的,我们将尾数最高位设为0,则,最小值为2^(-1022-52)=2**(-1074),此时为Number.MIN_VALUE

另外还有一些特殊值

  • 当指数是0,尾数是0时为0
  • 当指数全为1,尾数为0时为Infinity
  • 当指数全为1,尾数不为0时为NaN

string

字符串是由一系列16位无符号integer组成,最多有2**53-1个元素 每个元素在字符序列中占一个位置

转义字符

比如几种用16进制的表示法

  • \uXXXX 四个16进制,表示U+0000 and U+FFFF
  • \u{X}…\u{XXXXXX} 用来表示U+0000 and U+10FFFF,即全部unicode
  • \xXX 表示U+0000到U+00FF
Template Tags

模板字面量template literals,即使用``包含的字符串,其中可以使用插值,比如string text ${expression} string text

模板字面量还可以被函数解析,这被称为Tagged templates,调用时如fn``

fn的收个参数是模板字面量中各个非插值的字符串组成的数组,其余参数是各个插值的值

比如

let person = 'Mike';
let age = 28;

function myTag(strings, personExp, ageExp) {
  let str0 = strings[0]; // "That "
  let str1 = strings[1]; // " is a "
  let str2 = strings[2]; // "."

  let ageStr;
  if (ageExp > 99){
    ageStr = 'centenarian';
  } else {
    ageStr = 'youngster';
  }

  // We can even return a string built using a template literal
  return `${str0}${personExp}${str1}${ageStr}${str2}`;
}

let output = myTag`That ${ person } is a ${ age }.`;

console.log(output);
// That Mike is a youngster.
静态方法

String.fromCharCode()/String.fromCodePoint()
在es6之前每个字符的unicode被称为charCode,范围在0到ffff,之后每个字符的unicode被称为codePoint,范围在0到10ffff

这里的两个方法就是将对应的数字形式的码点转换成字符串

实例方法
  • 访问元素
    • str[pos]
    • str.charAt(pos)
    • str.charCodeAt(pos)
    • str.codePointAt(pos)
  • 大小写转换
    • toLowerCase()
    • toUpperCase()
  • concat(str [, ...strN ])
  • 是否包含
    • includes(searchString [, position])
    • endsWith(searchString [, length])
    • startsWith(searchString[, position])
    • indexOf(searchValue [, fromIndex]) 自fromIndex开始,从前往后检测,返回指定子串所在的位置,找不到返回-1
    • lastIndexOf(searchValue [, fromIndex]) 自fromIndex开始,从后往前找指定字符,返回第一个找到的字符位置,如果fromIndex超过最后一个字符,则从最后一个字符开始。 找不到返回-1
  • 填充
    • padEnd(targetLength [, padString]) 利用padstring填充当前字符串到targetlength,并返回新的字符串,默认填充空格
    • padStart(targetLength [, padString])
    • repeat(count) 将当前字符串重复count次,其中count取值0到+infinity以内的整数
  • 获取子字符串
    • slice(beginIndex[, endIndex]) 截取[begin,end)范围内的子字符串并返回
    • substring(indexStart [, indexEnd])
    • substr(start [, length])
  • 去空格
    • trim()
    • trimStart()
    • trimEnd()

symbol

每个symbol都是不同且不可变的,可以作为对象的key,每个symbol都有一个相关值即[[Description]],要么是字符串要么是undefined

symbol的创建有两种,一种是直接调用symbol,比如

    let sym1 = Symbol()

对应变量只能在作用域内可用,比如全局作用域。另一种是通过Symbol.for(),比如

    Symbol.for(key);

会在runtime-wide symbol registry定义一个symbol,不受realm限制,再次创建时返回原来的值,使用Symbol.keyFor()读取对应的key。

object类型

对象在逻辑上是属性的集合,对象属性的key,要么是string,要么是symbol,也可以是整数索引,比如Array对象。

property attribbute

属性分为两种

  • date property
    • [[value]]
    • [[Writable]] 是否可写
    • [[Enumerable]] 是否可枚举
    • [[Configurable]] 是否可删除
  • accessor property
    • [[Get]]
    • [[Set]]
    • [[Enumerable]]
    • [[Configurable]]

每种属性右有四种特性(attributes)用来定义对应属性的行为。

通过字面量或直接添加的属性 writable, enumerable, and configurable默认为true.

当使用property descriptor创建属性时,不一定要包含所有特性,被忽略的会被设置为undefined或false,如果是修改属性,被忽略的不会改变。

定义或访问property attribute的方法包括

  • Object.defineProperty(obj, prop, descriptor)

定义或修改对象属性,具体可分为前面讲的数据属性和访问器属性两种,比如

const object1 = {};

Object.defineProperty(object1, 'property1', {
  value: 42,
  writable: false
});
  • Object.defineProperties(obj, props) 一次性修改多个属性,比如
var obj = {};
Object.defineProperties(obj, {
  'property1': {
    value: true,
    writable: true
  },
  'property2': {
    value: 'Hello',
    writable: false
  }
  // etc. etc.
});
  • Object.getOwnPropertyDescriptor(obj, prop)
    获取相关属性的attr
var o, d;

o = { get foo() { return 17; } };
d = Object.getOwnPropertyDescriptor(o, 'foo');
// d is {
//   configurable: true,
//   enumerable: true,
//   get: /*the getter function*/,
//   set: undefined
// }
  • Object.getOwnPropertyDescriptors(obj)
    获取多个
var obj = {};
Object.defineProperties(obj, {
  'property1': {
    value: true,
    writable: true
  },
  'property2': {
    value: 'Hello',
    writable: false
  }
  // etc. etc.
});

Object.getOwnPropertyDescriptors(obj)
//{
    "property1": {
        "value": true,
        "writable": true,
        "enumerable": false,
        "configurable": false
    },
    "property2": {
        "value": "Hello",
        "writable": false,
        "enumerable": false,
        "configurable": false
    }
}
  • Object.getOwnPropertyNames(obj) 返回自身属性key组成的数组,不包含symbol属性
  • Object.getOwnPropertySymbols(obj) 返回symbol属性key组成的数组

属性的可扩展性

相关有三种程度的不可扩展,其中

最简单的preventextensions,不允许添加新属性

其次为seal,在前者基础上,会让所有属性的configurable为false,即不能删除属性和修改特性

最后是freeze,在前者基础上使所有属性readonly

image.png

在修改对象扩展性时,原型对象作为普通的__proto__属性看待。

对象的属性默认是extensible,一旦一个属性设置为不可扩展,就不能改回。

相关的方法包括

  • Object.preventExtensions(obj)
  • Object.isExtensible(obj)
  • Object.seal(obj)
  • Object.isSealed(obj)
  • Object.freeze()
  • Object.isFrozen()

prototype attribute

prototype attribute用来指定属性继承的对象,注意区别prototype property

  • Object.getPrototypeOf(obj) 返回原型对象
var proto = {};
var obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true
  • Object.setPrototypeOf(obj, prototype)
  • isPrototypeOf

Well-Known Symbols

Symbol有些静态属性,经常用作对象的属性key,其值用来作为一些算法的扩展,而不会影响当前的语法,比如对一个对象迭代时,会调用其Symbol.iterator属性

     var myIterable = {}
     myIterable[Symbol.iterator] = 
     function* () {
        yield 1;
        yield 2;
        yield 3;
     };
    [...myIterable] // [1, 2, 3]

属性的枚举和自身

属性检测,这几个方法都包含symbol属性

  • 自身且可枚举的属性 propertyIsEnumerable
  • 自身所有属性 hasOwnProperty
  • 自身和原型链上所有属性 in

枚举

  • 自身可枚举 Object.keys()等(不包括symbol)
  • 自身或原型链可枚举 for...in(不包括symbol)
  • 自身不区分可枚举 getOwnPropertyNames(不包括symbol)
  • getOwnPropertySymbols (只有symbol)

Object对象

js中的任何对象都是Object的实例,包括其他内置对象,比如Function,Array等。

需要注意的是,Object还是Function的实例,因为它本身也是构造函数,即

Function.__proto__===Function.prototype//true
Function.prototype.__proto__===Object.prototype//true,即Function instanceof Object
//原型链 Function=>Function.prototype=>Object.prototype,以下类似

Object.__proto__===Function.prototype//true,即Object instanceof Function
Array.__proto__===Function.prototype //true

function a(){}
a.__proto__===Function.prototype//true

原型链具体参考js中的面向对象编程和函数式编程

静态方法

  • Object.assign(target, ...sources) 将source的自身可枚举属性从source复制到target,包括string和symbol属性
  • Object.create(proto, propertiesObject) 使用指定对象为新创建对象的原型,还可以另外添加属性,格式相当于Object.defineProperties()的第二个参数
  • 迭代方法
    • Object.entries(obj)
    • Object.fromEntries()
    • Object.keys(obj)
    • Object.values(obj)
  • Object.is(value1, value2); 判断两个参数是否相等,和===的区别是,前者会把两个NaN作为相等,而不把±0看作相等。

实例属性

  • __proto__

实例方法

  • hasOwnProperty(prop)
  • isPrototypeOf(object)
  • propertyIsEnumerable(prop)

类型转换

类型转换原始类型之间比较简单,见图片。

image.png

对象转化为原始类型就比较复杂,因为每个对象可能不止一种原始类型的表示。

学习对象转化原始类型之前先熟悉两个方法

  • toString() 用来转化为字符串,很多内置对象会重写
  • valueof() 一般默认为返回对象本身

对象转化为原始类型会按照三种算法

  • prefer-string 优先转化为字符串,即首先调用toString()方法,如果得到原始值则返回,否则调用valueOf(),如果得到原始值,则返回,否则失败
  • prefer-number 有先转化为数字,两个方法的调用和上一种相反
  • no-preference 没有明显倾向,内置类型中除了Date优先转化为string,其他优先number

比如 Number([]) 对象首先转化为原始类型,即先调用valueof(),失败再调用tostring,得空字符串,空字符串转化为数字为0,类似Number([2])

如果要转化为布尔值不遵守以上,因为都转化为true, 如果要转化为字符串,首先使用prefer-string算法,然后将其结果转化为字符串,比如调用 String() 如果要转化为数字,首先使用prefer-number算法, 然后将其结果转化为数字

注意: 如果两个对象相加,则先使用第三种算法转化为原始类型,如果其中包括字符串,则结果为字符串连接,否则数字相加

全局对象

全局对象会在程序代码进入任何执行上下文之前创建,宿主环境可以挂载自定义的属性。

程序代码在运行中所用到的内置对象都是通过全局对象获取的。

全局常量

  • Infinity
  • NaN
  • undefined
  • globalThis

全局函数

  • eval()
  • isFinite()
  • isNaN()
  • parseFloat(string)
  • parseInt(string, radix)
  • encodeURI()/decodeURI()
  • encodeURIComponent()/decodeURIComponent()

全局构造函数

这里内容较多,部分会在后面章节展开

  • Array
  • Date
  • Error
  • Map/WeakMap
  • Set/WeakSet
  • Proxy
  • Binary Data Operation

全局命名空间

  • JSON
  • Atomics
  • Math
  • Reflect

Array

数组,length最大2**32-1

静态方法

  • Array.from(arrayLike, function mapFn(element, index, array) { ... }, thisArg) 将类数组或者迭代器转化为数组,第二个是map函数
  • Array.isArray(value)
  • Array.of(element0, element1, ... , elementN)

实例方法

  • concat(value0, value1, ... , valueN)
  • 迭代
    • entries()
    • keys()
    • values()
  • 填充
    • copyWithin(target, start, end) 从数组内一部分复制到另一个位置而不改变位置。其中 target指的是复制到的目标位置,如果是负的就从后往前数。后面两个是起始位置,默认是数组始末位置
    • fill(value, start, end) 将start到end的位置填充为value
  • 对每个元素执行回调
    • every
    • filter
    • forEach
    • map
    • some
  • 是否包含
    • find 返回第一个返回值为true的元素,如果没有则返回undefined
    • findIndex 返回第一个返回值为true的元素,如果没有则返回undefined
    • includes(searchElement, fromIndex) 返回布尔值
    • indexOf(searchElement, fromIndex)
    • lastIndexOf(searchElement, fromIndex)
  • 排序
    • reverse()
    • sort(function compareFn(firstEl, secondEl) { ... })

定义排序规则的函数,默认会将元素转化为字符串后按code point排序。

如果排序函数返回值大于0,两者会交换顺序,否则不变。

比如(a, b) => a - b可以用来正序排列

  • reducer
    • reduce(function callbackFn(accumulator, currentValue, index, array) { ... }, initialValue)
    • reduceRight(function callbackFn(accumulator, currentValue, index, array) { ... }, initialValue)
  • 栈和队列方法
    • push
    • pop
    • shift
    • unshift
  • 获取子数组
    • slice(start, end) 类似字符串的slice
    • splice(start, deleteCount, item1, item2, itemN) 从start开始删除deleteCount个,然后在对应位置添加元素。如果start是负的,从后往前数。

词法结构

源码

源码使用任何unicode code point表示,范围是U+0000 to U+10FFFF,区分大小写。
js使用utf-16编码,源码通常使用utf-8,运行时会进行转换。 其中字符串中每个元素都是一个16位的字符。

每个源码在内存中可能有多种表示,比如

console.log('\u00e9') // => é
console.log('\u0065\u0301') // => é

这样外形一样的代码并不是同一个标识符,为了统一需要实现规范化,可以使用String.prototype.normalize()

注释

分为单行注释和多行注释

字面量

字面量是在程序中直接使用的值,比如数字字面量,字符串字面量,布尔等

标识符和保留字

标识符identifier就是一个name,用来表示常量、变量、属性、函数、class以及为循环提供label 保留字不能或限制用作标识符,防止歧义

表达式和操作符

基本表达式

包括常量、字面量、关键字或者变量引用

this

this指的是 invocation context

对于全局环境记录,this指的[[GlobalThisValue]],可以是任何对象。
对于函数环境记录this指的是[[ThisValue]],用来调用对应函数。
this在环境记录中一般作为一个对象挂载相关值。

  • 全局上下文 可以作为全局对象的globalThis访问,不区分是不是严格模式
  • 函数上下文 一个函数中this的值取决于怎么调用,严格模式默认undefined,非严格模式默认globalThis,这被称为默认绑定。即调用方式为fun(),其他调用方式还有
    • 作为对象的方法,函数内的this指向该对象,这被称为隐式绑定,注意两点,1.绑定到最近的对象,比如o.a.b(),b函数绑定到a对象;2.如果将函数作为对象赋值给另一个变量,就会成为默认绑定,比如var b=o.b;b(),这被称为绑定丢失
    • call、apply绑定,用来将函数指定this并执行,在非严格模式下,作为this的参数如果不是对象会先转化为对象,其中null和undefined会转换为globalThis,其他原始值会使用相应构造函数转换为对象。
    • bind,调用f.bind(someObject)会返回一个this指向someObject的函数,且只能bind调用一次
    • new调用,其中的this指向返回的实例对象,如果返回的是另一个对象,则其中的this无效。以上绑定方法中new调用优先级最高,其次是不同call,apply和bind绑定,然后是隐式绑定最后是默认绑定
    • 箭头函数中的this和外部上下文的this指向相同,不能用call,apply和bind绑定方式绑定,其中的this参数会被忽略。
    • 作为dom事件处理函数,this指向触发事件的元素
    • 作为内联事件处理函数,this指向监听器所在的dom元素
  • class上下文 类本质上就是函数,也有一些区别。 类中的所有非静态方法会添加到this上,派生类的构造函数没有初始的this绑定,调用super才会进行。 另外类中的函数和其他普通函数一样,方法中的this取决于它们如何被调用,因此有必要在构造函数中进行绑定。
class Car {
  constructor() {
    // Bind sayBye but not sayHi to show the difference
    this.sayBye = this.sayBye.bind(this);
  }
  sayHi() {
    console.log(`Hello from ${this.name}`);
  }
  get name() {
    return 'Ferrari';
  }
}

new

实例化一个构造函数或类

  1. 创建一个空对象,即{}
  2. 设置该对象的[[prototype]]为被实例化的构造函数或类
  3. 将这个新对象作为this的上下文
  4. 如果被调用的构造函数没返回对象,则返回this

属性访问表达式

expression ?. identifier
expression ?.[ expression ]

可以使用条件属性访问

调用表达式

调用一个函数或方法,也可以条件调用

o.m() // Regular property access, regular invocation
o?.m() // Conditional property access, regular invocation
o.m?.() // Regular property access, conditional invocation

操作符优先级

参考这里

数学表达式

这里关注位操作符,具体看这

关系表达式

==

比较顺序为

  • 如果同一个类型,则按严格相等比较,
  • 如果一个值是null一个是undefined,则相等
  • 如果一个是数字,一个是字符串,则将字符串转化为数字再比较
  • 如果一个是布尔值,则将其转化为1或0,再进行其他比较,比如‘1’==true
  • 如果一个是对象,一个是原始值,则将对象转化为原始类型,注意Date先调用toString再调用valueof,其他相反
  • 其他组合都是错

===

判断顺序为

  • 如果类型不同,则不等
  • 如果都是null或undefined,或相同的布尔值,相同的数字,相同的字符串,则相等
  • 如果其中一个是NaN,则不相等
  • 如果是引用类型则判断是不是同一个引用

还有个类似的api是object.is() 和严格相等的区别是+0和-0不相等,但是NaN和NaN相等,其他一样

比较操作符

比较步骤为

  • 如果是对象,先转化为原始值
  • 如果转化后两者是string,则按照字母表顺序比较
  • 如果至少有一个不是string,两者都被转化为数字,0和-0相等,如果其中一个是NaN则会返回false

其他

  • First-Defined (??) 如果左侧是undefined或null,则返回右侧,否则返回左侧

正则表达式

用来匹配一个模式的字符串

参考这里

控制对象

Iteration

Iteration并不是一个内置对象也不是一个新语法,而是一种协议,可以被任何对象遵循某些约定来实现。包括两个协议。

有三个相关对象需要了解

  1. iterable对象,比如Array等可迭代对象,有一个特殊的迭代器方法(方法名为Symbol.iterator),返回一个迭代器对象、 其中generator函数调用就返回迭代器对象,因此可以作为这个迭代器方法。

  2. iterator对象,迭代器对象执行迭代,有一个next()方法,返回迭代结果对象

  3. iteration result,迭代结果,用来保存迭代的每一步结果,包含属性done和value,当done是false时,value包含对应值,否则value是undefined,除非采用return显式返回,参考下一节

为了使一个可迭代对象执行迭代,首先调用它的迭代方法获取迭代器对象,然后重复调用后者的next方法,直到迭代结果done属性为true

The iterable protocol

可迭代协议允许js对象定义它们的迭代行为,有些内置类型是内置可迭代的,比如Array或Map,有些没有,比如Object。

为了可迭代,一个对象要实现@@iterator方法,即[Symbol.iterator]

var myIterable = {};
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};
[...myIterable]; // [1, 2, 3]

The iterator protocol

定义了一个标准的方式来产生一系列values

当一个对象按照下面的语义实现next()方法时才是一个迭代器 next()是一个无参数函数,返回一个应拥有以下两个属性的对象

  • done 布尔值,如果迭代器可以产生下一个值则为false,否则返回true,此时下一个value值可选
  • value 任何类型

generator

调用时返回一个generator对象,每次调用generator的next会执行一个yield,每次执行时会执行该yield和之前没执行过的代码。

其中yield的返回值可以通过next调用时传入,内次调用next返回的对象value是yield后面的表达式。

yield每次可以处理一个值,yield*可以接一个iterable对象,会对其展开。

function* bar() {
  yield 'x';
  yield* foo();
  yield 'y';
}
 
// 等同于
function* bar() {
  yield 'x';
  yield 'a';
  yield 'b';
  yield 'y';
}

当在yield后面添加异步操作数时,比如Promise,默认会将promise直接返回。如果想要处理异步行为,需要进一步封装,比如使用async函数。

generator函数没有箭头函数的格式

return 返回值

当在generator中使用return时可以提前结束,当最后一个yield执行完,再次调用next时默认done为true,value为undefined,如果存在return语句,value为return返回的值。

return返回值只影响手动迭代,对for...of等无效

yield表达式的值

当调用generator函数时不会执行其中的代码,返回一个迭代器对象。 第一次调用next时,会执行第一个yield表达式和之前的代码,返回yield后的表达式的计算后的值,第一次的next传参会被丢弃。

以后每次调用next的参数,会赋予前一个yield的返回值。

即yield会比next少一次,从第二次开始的next参数会赋值给前一个yield的返回值

function* smallNumbers() {
 console.log("next() invoked the first time; argument discarded");
 let y1 = yield 1; // y1 == "b"
 console.log("next() invoked a second time with argument", y1);
 let y2 = yield 2; // y2 == "c"
 console.log("next() invoked a third time with argument", y2);
 let y3 = yield 3; // y3 == "d"
 console.log("next() invoked a fourth time with argument", y3);
 return 4; }
let g = smallNumbers();
console.log("generator created; no code runs yet");
let n1 = g.next("a"); // n1.value == 1
console.log("generator yielded", n1.value);
let n2 = g.next("b"); // n2.value == 2
console.log("generator yielded", n2.value);
let n3 = g.next("c"); // n3.value == 3
console.log("generator yielded", n3.value);
let n4 = g.next("d"); // n4 == { value: 4, done: true }
console.log("generator returned", n4.value);

Generator的return和throw方法

generator不仅包含next方法,还有return和throw,调用return可以让下一次的迭代结果为done为true,value为return的参数

throw会报错并结束迭代,可以对对应yield进行错误捕获,从而可以对generator继续迭代。

异步编程

在js的异步编程中,除了之前的回调,还有promise,async函数和异步迭代等

回调

回调是一个函数,会在当条件满足(比如定时器)或事件触发(事件监听回调)时执行对应函数。

promise

promise是一个对象,用来表示一个异步计算结果。

其本质也是回调,但使得嵌套回调成为链式,而且便于错误捕获

promise的几个状态,注意区分resolved和settled,参考这里

当resolved另一个promise时不代表settled.

async 函数

返回一个promise,其中可以使用await对各种promise进行分步处理。

异步迭代

前面我们讲了for...of进行迭代的迭代器,可以通过添加Symbol.iterator方法(比如generator函数)使对象可迭代

这里我们讨论异步迭代器,可以在async函数中使用for/await进行异步迭代,一个异步迭代对象有个Symbol.asyncIterator方法,调用后返回异步迭代器,异步迭代器每次调用返回会resolves 迭代结果对象的promise。 我们可以通过async generator来实现该方法。

for/await除了可以迭代异步迭代器,还可以迭代元素为promise的数组

模块

参考这里

代码的每个执行环境是一个agent,比如window或worker,其中会包含一个执行上下文栈用来跟踪代码的执行。
当前的执行上下文又被称为作用域,如果一个变量没在当前作用域就无法访问,作用域嵌套形成作用域链,内部的作用域可以访问外部作用域。 js中的代码都是用来处理变量,常见的作用域包括函数作用域和全局作用域,函数作用域之间不方便共享变量,全局作用域中script要保证引入顺序以及变量的访问和修改不宜维护,因此引入了模块。

es module被拆成三段

  1. Construction 从入口开始一层一层进行模块解析,下载后解析成一个个module record
  2. Instantiation js引擎创建module environment record用来管理module record中的变量,即按照深度优先的遍历方法初始化各个模块中使用到的变量(不会赋值),模块的实例多次引用时会共享。
  3. Evaluation js引擎执行顶层代码,并将对应变量赋值,同时也会执行副作用代码。

和cmj的区别

  1. es module的异步在于分成了三个阶段,每个阶段分别执行。而cmj不是异步的,因为本身就很快,不需要网络请求。
  2. es module的module specifier不能包含变量,因为模块解析时变量还没有赋值,实在需要可以使用import()
  3. es module导出或引入的同一个变量在同一个内存存储,即live binding,从而解决了循环引用的问题,导出的模块可以任意修改,但导入的模块只能在导入一个变量时修改其属性。 cmj导出的是一个拷贝。
  4. es module可以静态分析实现tree shaking等。

函数

是带有[[call]]内置方法的对象,所有函数都是Function的实例

可以说,一个函数相当于一个子程序,可以被外部的代码调用,也可以在内部递归调用。就像一个程序本身,一个函数也是由一系列语句组成,即函数体。 在js中,函数是一等对象,因为既可以像其他对象一样有属性和方法,而且可以被调用

class

class是一个创建对象的模板,js中的class基于原型,但也有另外的一些用法。

根据犀牛书第七版 在js中,a class is a set of objects that inherit properties from the same prototype object.

if two objects inherit proper‐ ties (generally function-valued properties, or methods) from the same prototype, then we say that those objects are instances of the same class.

比如我们可以用过Object.create创建class,class实现了继承,但class实例通常需要进一步初始化,比如工厂函数

function range(from, to) {
 let r = Object.create(range.methods);
//初始化
 r.from = from;
 r.to = to;
 return r; }

range.methods={}

当然也可以通过构造函数(通常首字母大写)对新创建的对象初始化,然后通过new实例化

function Range(from, to) {

 this.from = from;
 this.to = to; }

Range.prototype ={}

除了上面两个方法,有了class语法来实现以上功能。