概述
更详细可参考这篇。
数据类型
在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
在修改对象扩展性时,原型对象作为普通的__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)
类型转换
类型转换原始类型之间比较简单,见图片。
对象转化为原始类型就比较复杂,因为每个对象可能不止一种原始类型的表示。
学习对象转化原始类型之前先熟悉两个方法
- 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元素
- 作为对象的方法,函数内的this指向该对象,这被称为隐式绑定,注意两点,1.绑定到最近的对象,比如
- 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
实例化一个构造函数或类
- 创建一个空对象,即{}
- 设置该对象的[[prototype]]为被实例化的构造函数或类
- 将这个新对象作为this的上下文
- 如果被调用的构造函数没返回对象,则返回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并不是一个内置对象也不是一个新语法,而是一种协议,可以被任何对象遵循某些约定来实现。包括两个协议。
有三个相关对象需要了解
-
iterable对象,比如Array等可迭代对象,有一个特殊的迭代器方法(方法名为Symbol.iterator),返回一个迭代器对象、 其中generator函数调用就返回迭代器对象,因此可以作为这个迭代器方法。
-
iterator对象,迭代器对象执行迭代,有一个next()方法,返回迭代结果对象
-
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被拆成三段
- Construction 从入口开始一层一层进行模块解析,下载后解析成一个个module record
- Instantiation js引擎创建module environment record用来管理module record中的变量,即按照深度优先的遍历方法初始化各个模块中使用到的变量(不会赋值),模块的实例多次引用时会共享。
- Evaluation js引擎执行顶层代码,并将对应变量赋值,同时也会执行副作用代码。
和cmj的区别
- es module的异步在于分成了三个阶段,每个阶段分别执行。而cmj不是异步的,因为本身就很快,不需要网络请求。
- es module的module specifier不能包含变量,因为模块解析时变量还没有赋值,实在需要可以使用import()
- es module导出或引入的同一个变量在同一个内存存储,即live binding,从而解决了循环引用的问题,导出的模块可以任意修改,但导入的模块只能在导入一个变量时修改其属性。 cmj导出的是一个拷贝。
- 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语法来实现以上功能。