一个完整的 JavaScript 实现是由以下 3 个不同部分组成的:
核心(ECMAScript)
文档对象模型(DOM: Document Object Model)
浏览器对象模型(BOM: Browser Object Model)
ECMAScript 并不与任何具体浏览器相绑定,没有用于任何用户输入输出的方法。
可以为不同种类的宿主环境提供核心的脚本编程能力,因此核心的脚本语言是与任何特定的宿主环境分开进行规定的。
Web 浏览器对于 ECMAScript 来说是一个宿主环境,但并不是唯一的宿主环境。
ECMAScript 描述了以下内容:语法、类型、语句、关键字、保留字、运算符、对象
ECMAScript 仅仅是一个描述 ,定义了脚本语言的所有属性、方法和对象。
DOM 是 HTML 和 XML 的应用程序接口(API)。
DOM 将把整个页面规划成由节点层级构成的文档。
HTML 或 XML 页面的每个部分都是一个节点的衍生物。
BOM 独立于内容,可以对浏览器窗口进行访问和操作。
可以移动窗口、改变状态栏中的文本以及执行其他与页面内容不直接相关的动作。
它只是 JavaScript 的一个部分,没有任何相关的标准。
弹出新的浏览器窗口
移动、关闭浏览器窗口以及调整窗口大小
提供 Web 浏览器详细信息的定位对象
提供用户屏幕分辨率详细信息的屏幕对象
对 cookie 的支持
IE 扩展了 BOM,加入了 ActiveXObject 类,可以通过 JavaScript 实例化 ActiveX 对象
每种浏览器都有自己的 BOM 实现: Window 对象、Navigator 对象、Screen 对象、History 对象、Location 对象
基本 声明/定义
var
: 没有块作用域,变量会提升到顶端。
let
: 块作用域(Block Scope),在循环中使用的变量没有重新声明循环外的变量,循环内才可见。
const
: 常量,与 let 变量类似,但不能重新赋值。
class
: 特殊的函数,有两个组成部分:类表达式和类声明。
function
: 函数,被设计为执行特定任务的代码块。
关键字 break、case、catch、continue、default、delete、do、else、finally、for、if、in、instanceof、new、return、switch、this、throw、try、typeof、void、while、with、debugger、exports、import、static、super、async、wait
常用函数 Symbol、Number、Object、Boolean、String、Function、ArrayBuffer、AudioBufferSourceNode、Blob、FileReader、Map、Promise、Range、RegExp、Set、TypeError、Uint8Array、Uint32Array、WeakMap、WeakSet、Worker
类型 五种可包含值的数据类型
字符串(string)
数字(number)
布尔(boolean)
对象(object)
函数(function)
三种对象类型
对象(Object)
日期(Date)
数组(Array)
两种不能包含值的数据类型:
值
存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
5 种原始类型:Undefined、Null、Boolean、Number 和 String
存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。
类型判断 typeof 返回下列值之一:
undefined
如果变量是 Undefined 类型的
boolean
如果变量是 Boolean 类型的
number
如果变量是 Number 类型的
string
如果变量是 String 类型的
object
如果变量是一种引用类型或 Null 类型的
instanceof 在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 “object”。ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题。
1 2 3 4 5 6 var a = new String ('a' )var arr = []console .log(typeof a); console .log(a instanceof String ); console .log(typeof arr); console .log(arr instanceof Array );
Object.prototype.toString.call/apply 输出对象的类
Object.prototype.toString.call('')
: [object String]
Object.prototype.toString.call(1)
: [object Number]
Object.prototype.toString.call(null)
: [object Null]
Object.prototype.toString.call(undefined)
: [object Undefined]
Object.prototype.toString.call({})
: [object Object]
Object.prototype.toString.call(Symbol())
: [object Symbol]
Object.prototype.toString.call([])
: [object Array]
Object.prototype.toString.call(new Function())
: [object Function]
Object.prototype.toString.call(new Set())
: [object Set]
Object.prototype.toString.call(new Date())
: [object Date]
constructor 1 2 3 4 5 6 7 "Bill" .constructor (3.14 ).constructor false .constructor [1 ,2 ,3 ,4 ].constructor {name :'Bill' , age :62 }.constructor new Date ().constructor function ( ) {}.constructor
引用类型 引用类型通常叫做类(class),遇到引用值,所处理的就是对象。
对象是由 new 运算符加上要实例化的对象的名字创建的。
1 2 3 4 5 var o = new Object ();var o = new Object ;var d = new Date ;
Object 自身用处不大,ECMAScript 中的所有对象都由这个对象继承而来,Object 对象中的所有属性和方法都会出现在其他对象中。
属性:
constructor
对创建对象的函数的引用(指针)。对于 Object 对象,该指针指向原始的 Object() 函数。
Prototype
对该对象的对象原型的引用。对于所有的对象,它默认返回 Object 对象的一个实例。
方法:
hasOwnProperty(property)
判断对象是否有某个特定的属性。必须用字符串指定该属性。(例如,o.hasOwnProperty(“name”))
IsPrototypeOf(object)
判断该对象是否为另一个对象的原型。
PropertyIsEnumerable
判断给定的属性是否可以用 for…in 语句进行枚举。
ToString()
返回对象的原始字符串表示。对于 Object 对象,ECMA-262 没有定义这个值,所以不同的 ECMAScript 实现具有不同的值。
ValueOf()
返回最适合该对象的原始值。对于许多对象,该方法返回的值都与 ToString() 的返回值相同。
__proto__
已经从 Web 标准中删除
constructor 1 2 3 4 5 6 7 8 9 10 var o = {};o.constructor === Object ; var o = new Object ;o.constructor === Object ; var a = [];a.constructor === Array ; var a = new Array ;a.constructor === Array var n = new Number (3 );n.constructor === Number ;
assign 将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
拷贝 symbol 类型的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 const target = { a : 1 , b : 2 };const source = { b : 4 , c : 5 };const returnedTarget = Object .assign(target, source);console .log(target);console .log(returnedTarget);const o1 = { a : 1 , b : 1 , c : 1 };const o2 = { b : 2 , c : 2 };const o3 = { c : 3 };const obj = Object .assign({}, o1, o2, o3);console .log(obj); const obj = Object .create({foo : 1 }, { bar: { value: 2 }, baz: { value: 3 , enumerable: true } }); const copy = Object .assign({}, obj);console .log(copy); const v1 = "abc" ;const v2 = true ;const v3 = 10 ;const v4 = Symbol ("foo" )const obj = Object .assign({}, v1, null , v2, undefined , v3, v4); console .log(obj); const target = Object .defineProperty({}, "foo" , { value: 1 , writable: false }); Object .assign(target, {bar : 2 }, {foo2 : 3 , foo : 3 , foo3 : 3 }, {baz : 4 });console .log(target.bar); console .log(target.foo2); console .log(target.foo); console .log(target.foo3); console .log(target.baz);
Polyfill
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 if (typeof Object .assign != 'function' ) { Object .defineProperty(Object , "assign" , { value: function assign (target, varArgs ) { 'use strict' ; if (target == null ) { throw new TypeError ('Cannot convert undefined or null to object' ); } let to = Object (target); for (var index = 1 ; index < arguments .length; index++) { var nextSource = arguments [index]; if (nextSource != null ) { for (let nextKey in nextSource) { if (Object .prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true , configurable: true }); }
create 创建一个新对象,使用现有的对象来提供新创建的对象的proto 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 const person = { isHuman: false , printIntroduction: function ( ) { console .log(`My name is ${this .name} . Am I human? ${this .isHuman} ` ); } }; const me = Object .create(person);me.name = "Matthew" ; me.isHuman = true ; me.printIntroduction(); Object .create(null );var o = {};o = Object .create(Object .prototype); o = Object .create(Object .prototype, { foo: { writable:true , configurable:true , value: "hello" }, bar: { configurable: false , get : function() { return 10 }, set : function(value) { console .log("Setting `o.bar` to" , value); } } }); function Constructor ( ) {}o = new Constructor(); o = Object .create(Constructor.prototype); o = Object .create({}, { p: { value: 42 , writable: true , enumerable: true , configurable: true } });
可选。如果没有指定为 undefined,则是要添加到新创建对象的不可枚举(默认)属性对象的属性描述符以及相应的属性名称。
defineProperty 在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 var obj = {};Object .defineProperty(obj, 'name' , { value : 'n' });var obj = {};var descriptor = Object .create(null ); descriptor.value = 'static' ; Object .defineProperty(obj, 'key' , descriptor);Object .defineProperty(obj, "key" , { enumerable: false , configurable: false , writable: false , value: "static" }); var o = {};Object .defineProperty(o, "a" , { value : 1 , enumerable :true });Object .defineProperty(o, "b" , { value : 2 , enumerable :false });Object .defineProperty(o, "c" , { value : 3 }); o.d = 4 ; for (var i in o) { console .log(i); } Object .keys(o); o.propertyIsEnumerable('a' ); o.propertyIsEnumerable('b' ); o.propertyIsEnumerable('c' );
defineProperties 直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 var obj = {};Object .defineProperties(obj, { 'property1' : { value: true , writable: true }, 'property2' : { value: 'Hello' , writable: false } });
entries 返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for…in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let obj = { foo : 'bar' , baz : 42 };console .log(Object .entries(obj)); let obj = { 0 : 'a' , 1 : 'b' , 2 : 'c' };console .log(Object .entries(obj)); let anObj = { 100 : 'a' , 2 : 'b' , 7 : 'c' };console .log(Object .entries(anObj)); let myObj = Object .create({}, { getFoo : { value() { return this .foo; } } });myObj.foo = 'bar' ; console .log(Object .entries(myObj)); console .log(Object .entries('foo' )); let obj = { a : 5 , b : 7 , c : 9 };for (const [key, value] of Object .entries(obj)) { console .log(`${key} ${value} ` ); } var map = new Map (Object .entries({ foo : "bar" , baz : 42 }));console .log(map);
freeze、isFrozen 冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。
1 2 3 4 5 6 7 8 9 const obj = { prop : 42 };Object .freeze(obj);obj.prop = 33 ; console .log(obj.prop); delete obj.prop; console .log(obj.prop); console .log(Object .isFrozen(obj)); Object .defineProperty(obj, 'ohai' , { value : 17 }); Object .setPrototypeOf(obj, { x : 20 });
fromEntries 把键值对列表转换为一个对象。
1 2 3 4 5 6 7 8 9 10 const entries = new Map ([ ['foo' , 'bar' ], ['baz' , 42 ] ]); console .log(Object .fromEntries(entries)); const arr = [ ['0' , 'a' ], ['1' , 'b' ], ['2' , 'c' ] ];console .log(Object .fromEntries(arr)); const object = Object .fromEntries(Object .entries({ a : 1 , b : 2 , c : 3 }) .map(([ key, val ] ) => [ key, val * 2 ])); console .log(object);
getOwnPropertyDescriptor 返回指定对象上一个自有属性对应的属性描述符。
1 2 3 4 5 6 7 8 9 let o = { get foo() { return 17 ; } };let d = Object .getOwnPropertyDescriptor(o, "foo" );
getOwnPropertyDescriptors 获取一个对象的所有自身属性的描述符。
getOwnPropertyNames 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
1 2 3 4 var arr = ["a" , "b" , "c" ];console .log(Object .getOwnPropertyNames(arr).sort()); var obj = { 0 : "a" , 1 : "b" , 2 : "c" };console .log(Object .getOwnPropertyNames(obj).sort());
getOwnPropertySymbols 返回一个给定对象自身的所有 Symbol 属性的数组。
1 2 3 4 var obj = {};var symbol = Symbol ();obj[symbol] = 123 ; Object .getOwnPropertySymbols(obj);
getPrototypeOf 返回指定对象的原型(内部[[Prototype]]
属性的值)。
1 2 3 4 5 const prototype1 = {};const object1 = Object .create(prototype1);console .log(Object .getPrototypeOf(object1) === prototype1); var reg = /a/ ;Object .getPrototypeOf(reg) === RegExp .prototype;
is 判断两个值是否是相同的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Object .is('foo' , 'foo' ); Object .is(window , window ); Object .is('foo' , 'bar' ); Object .is([], []); var foo = { a : 1 };var bar = { a : 1 };Object .is(foo, foo); Object .is(foo, bar); Object .is(null , null ); Object .is(0 , -0 ); Object .is(0 , +0 ); Object .is(-0 , -0 ); Object .is(NaN , 0 /0 );
isExtensible、preventExtensions
isExtensible
: 判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
preventExtensions
: 让一个对象变的不可扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13 var empty = {};Object .isExtensible(empty); Object .preventExtensions(empty);Object .isExtensible(empty); var sealed = Object .seal({});Object .isExtensible(sealed); var frozen = Object .freeze({});Object .isExtensible(frozen);
isSealed、seal
seal
: 封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
isSealed
: 判断一个对象是否被密封。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var obj = { prop: function ( ) {}, foo: 'bar' }; obj.foo = 'baz' ; obj.lumpy = 'woof' ; delete obj.prop;var o = Object .seal(obj);o === obj; Object .isSealed(obj); obj.foo = 'quux' ; Object .defineProperty(obj, 'foo' , { get : function() { return 'g' ; } }); obj.quaxxor = 'the friendly duck' ; delete obj.foo;function fail ( ) { 'use strict' ; delete obj.foo; obj.sparky = 'arf' ; } fail(); Object .defineProperty(obj, 'ohai' , { value: 17 }); Object .defineProperty(obj, 'foo' , { value: 'eit' });
keys 会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 var arr = ['a' , 'b' , 'c' ];console .log(Object .keys(arr)); var obj = { 0 : 'a' , 1 : 'b' , 2 : 'c' };console .log(Object .keys(obj)); var anObj = { 100 : 'a' , 2 : 'b' , 7 : 'c' };console .log(Object .keys(anObj)); var myObj = Object .create({}, { getFoo: { value: function ( ) { return this .foo; } } }); myObj.foo = 1 ; console .log(Object .keys(myObj));
Function 参数个数限制在65536
1 2 const sum = new Function ('a' , 'b' , 'return a + b' );console .log(sum(2 , 6 ));
arguments 已经从 Web 标准中删除。
无需明确指出参数名,就能访问它们。
arguments.length
: 参数个数。arguments.callee
: 当前正在执行的函数。
1 2 3 4 var function_name = new function (arg1, arg2, ..., argN, function_body )
1 2 3 4 5 6 7 8 9 var sayHi = new Function ("sName" , "sMessage" , "alert(\"Hello \" + sName + sMessage);" );function sayHi (sName, sMessage ) { alert("Hello " + sName + sMessage); } console .log(sayHi.length); console .log(sayHi.toString());
注意:尽管可以使用 Function 构造函数创建函数,但最好不要使用它,因为用它定义函数比用传统方式要慢得多。
不过,所有函数都应看作 Function 类的实例。
length 指明函数的形参个数。
1 2 3 4 function func1 ( ) {}function func2 (a, b ) {}console .log(func1.length); console .log(func2.length);
name 函数实例的名称。
1 2 3 4 5 6 7 (new Function ).name; var f = function ( ) {};var object = { someMethod: function ( ) {} }; console .log(f.name); console .log(object.someMethod.name);
prototype 属性存储了 Function 的原型对象。
toString 返回一个表示当前函数源代码的字符串。
AOP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 var foo = function (value ) { value[0 ]=2 ; console .log(`call:${value} ` ); value[0 ]=3 ; } Function .prototype.before = function (fn ) { var _this = this ; return function ( ) { fn.apply(this ,arguments ); return _this.apply(this ,arguments ); } } Function .prototype.after = function (fn ) { var _this = this ; return function ( ) { var r = _this.apply(this ,arguments ); fn.apply(this ,arguments ); return r; } } foo.before(function (value ) { console .log(`before:${value} ` ); }).after(function (value ) { console .log(`after:${value} ` ); })([1 ]);
call/apply
每个函数都包含两个非继承而来的方法:apply()和call()。
都是在特定的作用域中调用函数。
call
: 第一个参数用作 this 的对象。其他参数都直接传递给函数自身。
apply
: 有两个参数,用作 this 的对象和要传递给函数的参数的数组。
call、apply、bind的作用是改变函数运行时this的指向
1 2 3 4 5 6 7 8 9 10 11 function foo (age, position ) { return `${this .name} : ${age} , ${position} ` } var obj={ name: 'a' } console .log(foo.call(obj, 20 , 'web' )) console .log(foo.apply(obj, [20 , 'web' ])) obj.foo=foo; console .log(obj.foo(20 , 'web' ))
bind bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
1 2 3 4 5 6 7 8 9 10 11 12 const module = { x: 42 , getX: function ( ) { return this .x; } } const unboundGetX = module .getX;console .log(unboundGetX()); const boundGetX = unboundGetX.bind(module );console .log(boundGetX());
1 2 3 4 5 6 7 function .bind (thisArg[, arg1[, arg2[, ...]]] )
使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为 bind() 的参数写在 this 后面。
当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function list ( ) { return Array .prototype.slice.call(arguments ); } function addArguments (arg1, arg2 ) { return arg1 + arg2 } var list1 = list(1 , 2 , 3 ); var result1 = addArguments(1 , 2 ); var leadingThirtysevenList = list.bind(null , 37 );var addThirtySeven = addArguments.bind(null , 37 ); var list2 = leadingThirtysevenList(); var list3 = leadingThirtysevenList(1 , 2 , 3 ); var result2 = addThirtySeven(5 ); var result3 = addThirtySeven(5 , 10 );
setTimeout
1 2 3 4 5 6 7 8 9 10 11 12 function LateBloomer ( ) { this .petalCount = Math .ceil(Math .random() * 12 ) + 1 ; } LateBloomer.prototype.bloom = function ( ) { window .setTimeout(this .declare.bind(this ), 1000 ); }; LateBloomer.prototype.declare = function ( ) { console .log('I am a beautiful flower with ' + this .petalCount + ' petals!' ); }; var flower = new LateBloomer();flower.bloom();
Polyfill
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if (!Function .prototype.bind) (function ( ) { var slice = Array .prototype.slice; Function .prototype.bind = function ( ) { var thatFunc = this , thatArg = arguments [0 ]; var args = slice.call(arguments , 1 ); if (typeof thatFunc !== 'function' ) { throw new TypeError ('Function.prototype.bind - what is trying to be bound is not callable' ); } return function ( ) { var funcArgs = args.concat(slice.call(arguments )) return thatFunc.apply(thatArg, funcArgs); }; }; })();
Boolean 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var oBooleanObject = new Boolean (true );var oFalseObject = new Boolean (false );var bResult = oFalseObject && true ; console .log(Boolean ()); console .log(Boolean ('' )); console .log(Boolean (null )); console .log(Boolean (undefined )); console .log(Boolean (NaN )); console .log(Boolean (0 )); console .log(Boolean ('hello' )); console .log(Boolean (50 )); console .log(Boolean ({})); console .log(Boolean ([])); console .log(Boolean (Infinity )); console .log(Boolean (-Infinity ));
hasOwnProperty 对象自身属性中是否具有指定的属性。
1 2 3 4 5 6 7 8 9 10 11 o = new Object (); o.propOne = null ; o.hasOwnProperty('propOne' ); o.propTwo = undefined ; o.hasOwnProperty('propTwo' ); o = new Object (); o.prop = 'exists' ; o.hasOwnProperty('prop' ); o.hasOwnProperty('toString' ); o.hasOwnProperty('hasOwnProperty' );
isPrototypeOf 一个对象是否存在于另一个对象的原型链上。
1 2 3 4 5 6 7 8 9 10 function Foo ( ) {}function Bar ( ) {}function Baz ( ) {}Bar.prototype = Object .create(Foo.prototype); Baz.prototype = Object .create(Bar.prototype); var baz = new Baz();console .log(Baz.prototype.isPrototypeOf(baz)); console .log(Bar.prototype.isPrototypeOf(baz)); console .log(Foo.prototype.isPrototypeOf(baz)); console .log(Object .prototype.isPrototypeOf(baz));
propertyIsEnumerable 表示指定的属性是否可枚举。
1 2 3 4 5 6 7 8 9 10 11 12 13 var o = {};var a = [];o.prop = 'is enumerable' ; a[0 ] = 'is enumerable' ; o.propertyIsEnumerable('prop' ); a.propertyIsEnumerable(0 ); var a = ['is enumerable' ];a.propertyIsEnumerable(0 ); a.propertyIsEnumerable('length' ); Math .propertyIsEnumerable('random' ); this .propertyIsEnumerable('Math' );
toLocaleString 返回一个该对象的字符串表示。此方法被用于派生对象为了特定语言环境的目的而重载使用。
valueOf 返回指定对象的原始值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var array = ["ABC" , true , 12 , -5 ];console .log(array.valueOf() === array); var date = new Date (2013 , 7 , 18 , 23 , 11 , 59 , 230 );console .log(date.valueOf()); var num = 15.26540 ;console .log(num.valueOf()); var bool = true ;console .log(bool.valueOf() === bool); var newBool = new Boolean (true );console .log(newBool.valueOf() == newBool); console .log(newBool.valueOf() === newBool); function foo ( ) {}console .log( foo.valueOf() === foo ); var foo2 = new Function ("x" , "y" , "return x + y;" );console .log( foo2.valueOf() );var obj = {name : "张三" , age : 18 };console .log( obj.valueOf() === obj ); var str = "http://www.xyz.com" ;console .log( str.valueOf() === str ); var str2 = new String ("http://www.xyz.com" );console .log( str2.valueOf() === str2 );
setPrototypeOf 设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或 null。
values 返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for…in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
1 2 3 4 5 6 7 8 9 10 var obj = { foo : 'bar' , baz : 42 };console .log(Object .values(obj)); var obj = { 0 : 'a' , 1 : 'b' , 2 : 'c' };console .log(Object .values(obj)); var an_obj = { 100 : 'a' , 2 : 'b' , 7 : 'c' };console .log(Object .values(an_obj)); var my_obj = Object .create({}, { getFoo : { value : function ( ) { return this .foo; } } });my_obj.foo = 'bar' ; console .log(Object .values(my_obj)); console .log(Object .values('foo' ));
Number 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 var oNumberObject = new Number (68 );var iNumber = oNumberObject.valueOf();console .log(oNumberObject.toFixed(2 )); console .log(oNumberObject.toExponential(1 ));console .log(oNumberObject.toPrecision(1 )); console .log(oNumberObject.toPrecision(2 )); console .log(oNumberObject.toPrecision(3 )); console .log(oNumberObject.toString(2 )); console .log(oNumberObject.toString(8 )); console .log(oNumberObject.toString(16 )); console .log(oNumberObject.toString(36 )); console .log(Number .MAX_VALUE); console .log(Number .MIN_VALUE); console .log(Number .POSITIVE_INFINITY); console .log(Number .NEGATIVE_INFINITY); console .log(NaN == NaN ); console .log(Number ()); console .log(Number (false )); console .log(Number (true )); console .log(Number (undefined )); console .log(Number (null )); console .log(Number ('1.2' )); console .log(Number ('1.2.3' )); console .log(Number ({}));
String 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 var oStringObject = new String ("hello world" );console .log(oStringObject.valueOf() == oStringObject.toString()); console .log(new String (null ).toString()); console .log(String ()); console .log(String (undefined )); console .log(String ([])); console .log(String ([1 , 2 , 3 ])); console .log(String ({})); console .log(String (false )); console .log(String (() =>0 )); console .log(String (function ( ) {})); console .log(String ([[-1 ,2 ,[3 ]],[4 ,[5 ]]])); console .log(oStringObject.charAt(1 )); console .log(oStringObject.charCodeAt(1 )); console .log(String .fromCharCode(101 )); console .log("hello " .concat("world" )); console .log(oStringObject.indexOf("o" )); console .log("Blue Whale" .indexOf("l" , 5 )); console .log(oStringObject.lastIndexOf("o" )); console .log("1" .padStart(3 , "0" )); console .log("1" .padEnd(3 , "0" )); console .log("yellow" .localeCompare("brick" )); console .log("yellow" .localeCompare("yellow" )); console .log("yellow" .localeCompare("zoo" )); console .log(oStringObject.slice(3 )); console .log(oStringObject.substring(3 )); console .log(oStringObject.slice(3 , 7 )); console .log(oStringObject.substring(3 , 7 )); console .log(oStringObject.slice(-3 )); console .log(oStringObject.substring(-3 )); console .log(oStringObject.slice(3 , -4 )); console .log(oStringObject.substring(3 , -4 )); console .log(oStringObject.toLocaleUpperCase()); console .log(oStringObject.toUpperCase()); console .log(oStringObject.toLocaleLowerCase()); console .log(oStringObject.toLowerCase()); console .log(String .raw`\n\5\xxx\uuu\111` ); var str1 = 'Cats are the best!' ;console .log(str1.endsWith('best' , 17 )); var str = 'For more information, see Chapter 3.4.5.1' ;var re = /see (chapter \d+(\.\d)*)/i ;console .log(str.match(re));var greeting = ' Hello world! ' ;console .log(greeting.trim()); var str = 'To be, or not to be, that is the question.' ;console .log(str.includes('To be' ));
Array from 从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
1 2 3 4 5 6 console .log(Array .from('foo' )); console .log(Array .from([1 , 2 , 3 ], x => x + x));
isArray 确定传递的值是否是一个 Array。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Array .isArray([1 , 2 , 3 ]); Array .isArray({foo : 123 }); Array .isArray("foobar" ); Array .isArray(undefined ); Array .isArray([]);Array .isArray([1 ]);Array .isArray(new Array ());Array .isArray(new Array ('a' , 'b' , 'c' , 'd' ))Array .isArray(Array .prototype); Array .isArray();Array .isArray({});Array .isArray(null );Array .isArray(undefined );Array .isArray(17 );Array .isArray('Array' );Array .isArray(true );Array .isArray(false );Array .isArray(new Uint8Array (32 ))Array .isArray({ __proto__ : Array .prototype });
Polyfill
1 2 3 4 5 if (!Array .isArray) { Array .isArray = function (arg ) { return Object .prototype.toString.call(arg) === '[object Array]' ; }; }
of 创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
1 2 3 4 5 Array .of(7 ); Array .of(1 , 2 , 3 ); Array (7 ); Array (1 , 2 , 3 );
concat 合并两个或多个数组。不会更改现有数组,而是返回一个新数组。
1 2 3 4 const array1 = ['a' , 'b' , 'c' ];const array2 = ['d' , 'e' , 'f' ];const array3 = array1.concat(array2);console .log(array3);
copyWithin 浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
1 2 3 4 5 6 7 8 9 10 11 12 const array1 = ['a' , 'b' , 'c' , 'd' , 'e' ];console .log(array1.copyWithin(0 , 3 , 4 )); console .log(array1.copyWithin(1 , 3 )); [1 , 2 , 3 , 4 , 5 ].copyWithin(-2 ) [1 , 2 , 3 , 4 , 5 ].copyWithin(0 , 3 ) [1 , 2 , 3 , 4 , 5 ].copyWithin(0 , 3 , 4 ) [1 , 2 , 3 , 4 , 5 ].copyWithin(-2 , -3 , -1 ) [].copyWithin.call({length : 5 , 3 : 1 }, 0 , 3 ); var i32a = new Int32Array ([1 , 2 , 3 , 4 , 5 ]);i32a.copyWithin(0 , 2 ); [].copyWithin.call(new Int32Array ([1 , 2 , 3 , 4 , 5 ]), 0 , 3 , 4 );
entries 返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对。
1 2 3 4 const array1 = ['a' , 'b' , 'c' ];const iterator1 = array1.entries();console .log(iterator1.next().value); console .log(iterator1.next().value);
every 测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
1 2 3 const isBelowThreshold = (currentValue ) => currentValue < 40 ;const array1 = [1 , 30 , 39 , 29 , 10 , 13 ];console .log(array1.every(isBelowThreshold));
fill 用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
1 2 3 4 5 const array1 = [1 , 2 , 3 , 4 ];console .log(array1.fill(0 , 2 , 4 )); console .log(array1.fill(5 , 1 )); console .log(array1.fill(6 ));
filter 创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
1 2 3 4 const words = ['spray' , 'limit' , 'elite' , 'exuberant' , 'destruction' , 'present' ];const result = words.filter(word => word.length > 6 );console .log(result);
find 返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
1 2 3 4 const array1 = [5 , 12 , 8 , 130 , 44 ];const found = array1.find(element => element > 10 );console .log(found);
findIndex 返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。
1 2 3 4 const array1 = [5 , 12 , 8 , 130 , 44 ];const isLargeNumber = (element ) => element > 13 ;console .log(array1.findIndex(isLargeNumber));
flat 会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
1 2 3 4 5 6 7 8 9 10 var arr1 = [1 , 2 , [3 , 4 ]];arr1.flat(); var arr2 = [1 , 2 , [3 , 4 , [5 , 6 ]]];arr2.flat(); var arr3 = [1 , 2 , [3 , 4 , [5 , 6 ]]];arr3.flat(2 ); var arr4 = [1 , 2 , [3 , 4 , [5 , 6 , [7 , 8 , [9 , 10 ]]]]];arr4.flat(Infinity );
flatMap 首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。
它与 map 连着深度值为1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
1 2 3 4 5 6 7 var arr1 = [1 , 2 , 3 , 4 ];arr1.map(x => [x * 2 ]); arr1.flatMap(x => [x * 2 ]); arr1.flatMap(x => [[x * 2 ]]);
forEach 对数组的每个元素执行一次提供的函数。
1 2 3 const array1 = ['a' , 'b' , 'c' ];array1.forEach(element => console .log(element));
includes 判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
1 2 3 4 5 6 const array1 = [1 , 2 , 3 ];console .log(array1.includes(2 )); const pets = ['cat' , 'dog' , 'bat' ];console .log(pets.includes('cat' )); console .log(pets.includes('at' ));
indexOf、lastIndexOf
indexOf
: 返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
lastIndexOf
: 返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。
1 2 3 4 5 6 7 8 9 10 const beasts = ['ant' , 'bison' , 'camel' , 'duck' , 'bison' ];console .log(beasts.indexOf('bison' )); console .log(beasts.indexOf('bison' , 2 )); console .log(beasts.indexOf('giraffe' )); const animals = ['Dodo' , 'Tiger' , 'Penguin' , 'Dodo' ];console .log(animals.lastIndexOf('Dodo' )); console .log(animals.lastIndexOf('Tiger' ));
join 将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。
如果数组只有一个项目,那么将返回该项目而不使用分隔符。
1 2 3 4 5 const elements = ['Fire' , 'Air' , 'Water' ];console .log(elements.join()); console .log(elements.join('' )); console .log(elements.join('-' ));
keys 返回一个包含数组中每个索引键的Array Iterator对象。
1 2 3 4 5 const array1 = ['a' , 'b' , 'c' ];const iterator = array1.keys();for (const key of iterator) { console .log(key); }
map 创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
1 2 3 4 5 6 const array1 = [1 , 4 , 9 , 16 ];const map1 = array1.map(x => x * 2 );console .log(map1);
pop 从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
1 2 3 4 5 const plants = ['broccoli' , 'cauliflower' , 'cabbage' , 'kale' , 'tomato' ];console .log(plants.pop()); console .log(plants); plants.pop(); console .log(plants);
push 将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
1 2 3 4 5 6 const animals = ['pigs' , 'goats' , 'sheep' ];const count = animals.push('cows' );console .log(count); console .log(animals); animals.push('chickens' , 'cats' , 'dogs' ); console .log(animals);
reduce 对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
1 2 3 4 5 const array1 = [1 , 2 , 3 , 4 ];const reducer = (accumulator, currentValue ) => accumulator + currentValue;console .log(array1.reduce(reducer)); console .log(array1.reduce(reducer, 5 ));
reduceRight 接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
1 2 3 4 5 const array1 = [[0 , 1 ], [2 , 3 ], [4 , 5 ]].reduceRight( (accumulator, currentValue) => accumulator.concat(currentValue) ); console .log(array1);
reverse 将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。
1 2 3 4 5 6 const array1 = ['one' , 'two' , 'three' ];console .log('array1:' , array1); const reversed = array1.reverse();console .log('reversed:' , reversed); console .log('array1:' , array1);
shift 从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
1 2 3 4 const array1 = [1 , 2 , 3 ];const firstElement = array1.shift();console .log(array1); console .log(firstElement);
slice 返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
1 2 3 4 5 const animals = ['ant' , 'bison' , 'camel' , 'duck' , 'elephant' ];console .log(animals.slice(2 )); console .log(animals.slice(2 , 4 )); console .log(animals.slice(1 , 5 ));
some 测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。
1 2 3 4 5 const array = [1 , 2 , 3 , 4 , 5 ];const even = (element ) => element % 2 === 0 ;console .log(array.some(even));
sort 用原地算法 对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的
由于它取决于具体实现,因此无法保证排序的时间和空间复杂性。
1 2 3 4 5 6 7 8 9 10 const months = ['March' , 'Jan' , 'Feb' , 'Dec' ];months.sort(); console .log(months); const array1 = [1 , 30 , 4 , 21 , 100000 ];array1.sort(); console .log(array1); var numbers = [4 , 2 , 5 , 1 , 3 ]; numbers.sort((a, b ) => a - b); console .log(numbers);
splice 过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
1 2 3 4 5 6 7 const months = ['Jan' , 'March' , 'April' , 'June' ];months.splice(1 , 0 , 'Feb' ); console .log(months); months.splice(4 , 1 , 'May' ); console .log(months);
toLocaleString 返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 “,”)隔开。
1 2 3 4 const array1 = [1 , 'a' , new Date ('21 Dec 1997 14:12:00 UTC' )];const localeString = array1.toLocaleString('en' , {timeZone : "UTC" });console .log(localeString);
unshift 将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。
1 2 3 4 const array1 = [1 , 2 , 3 ];console .log(array1.unshift(4 , 5 )); console .log(array1);
values 返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值
1 2 3 4 5 const array1 = ['a' , 'b' , 'c' ];const iterator = array1.values();for (const value of iterator) { console .log(value); }
@@iterator 和 Array.prototype.values() 属性的初始值是同一个函数对象。
1 2 3 4 5 6 7 8 9 10 11 12 var arr = ['a' , 'b' , 'c' , 'd' , 'e' ];var eArr = arr[Symbol .iterator]();for (let letter of eArr) { console .log(letter); } var eArr = arr[Symbol .iterator]();console .log(eArr.next().value); console .log(eArr.next().value); console .log(eArr.next().value); console .log(eArr.next().value); console .log(eArr.next().value);
ArrayBuffer 用来表示通用的、固定长度的原始二进制数据缓冲区。
不能直接操作 ArrayBuffer 的内容,而是要通过类型数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
Promise 用于表示一个异步操作的最终完成 (或失败), 及其结果值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 const promise1 = new Promise (function (resolve, reject ) { setTimeout(function ( ) { resolve('foo' ); }, 300 ); }); promise1.then(function (value ) { console .log(value); }); console .log(promise1); var promiseCount = 0 ;function testPromise ( ) { let thisPromiseCount = ++promiseCount; let log = document .getElementById('log' ); log.insertAdjacentHTML('beforeend' , thisPromiseCount + ') 开始 (<small>同步代码开始</small>)<br/>' ); let p1 = new Promise ( (resolve, reject) => { log.insertAdjacentHTML('beforeend' , thisPromiseCount + ') Promise 开始 (<small>异步代码开始</small>)<br/>' ); window .setTimeout(function ( ) { resolve(thisPromiseCount); }, Math .random() * 2000 + 1000 ); } ); p1.then( function (val ) { log.insertAdjacentHTML('beforeend' , val + ') Promise 已填充完毕 (<small>异步代码结束</small>)<br/>' ); }) .catch( (reason) => { console .log('处理失败的 promise (' +reason+')' ); } ); log.insertAdjacentHTML('beforeend' , thisPromiseCount + ') Promise made (<small>同步代码结束</small>)<br/>' ); }
all 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
1 2 3 4 5 6 7 8 const promise1 = Promise .resolve(3 );const promise2 = 42 ;const promise3 = new Promise (function (resolve, reject ) { setTimeout(resolve, 100 , 'foo' ); }); Promise .all([promise1, promise2, promise3]).then(function (values ) { console .log(values); });
allSettled 返回一个在所有给定的promise已被决议或被拒绝后决议的promise,并带有一个对象数组,每个对象表示对应的promise结果。
1 2 3 4 5 6 const promise1 = Promise .resolve(3 );const promise2 = new Promise ((resolve, reject ) => setTimeout(reject, 100 , 'foo' ));const promises = [promise1, promise2];Promise .allSettled(promises).then((results ) => results.forEach((result ) => console .log(result.status)));
any 接收一个Promise可迭代对象,只要其中的一个 promise 完成,就返回那个已经有完成值的 promise 。如果可迭代对象中没有一个 promise 完成(即所有的 promises 都失败/拒绝),就返回一个拒绝的 promise,返回值还有待商榷:无非是拒绝原因数组或AggregateError类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。本质上,这个方法和Promise.all()是相反的。
race 返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
1 2 3 4 5 6 7 8 9 10 11 const promise1 = new Promise (function (resolve, reject ) { setTimeout(resolve, 500 , 'one' ); }); const promise2 = new Promise (function (resolve, reject ) { setTimeout(resolve, 100 , 'two' ); }); Promise .race([promise1, promise2]).then(function (value ) { console .log(value); });
reject 返回一个带有拒绝原因的Promise对象。
1 2 3 4 5 6 7 8 function resolved (result ) { console .log('Resolved' ); } function rejected (result ) { console .error(result); } Promise .reject(new Error ('fail' )).then(resolved, rejected);
resolve 返回一个以给定值解析后的Promise 对象。如果该值为promise,返回这个promise;如果这个值是thenable(即带有”then” 方法)),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
1 2 3 4 const promise1 = Promise .resolve(123 );promise1.then(function (value ) { console .log(value); });
Proxy 用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。
1 2 3 4 5 6 7 8 9 10 let handler = { get : function(target, name){ return name in target ? target[name] : 37 ; } }; let p = new Proxy ({}, handler);p.a = 1 ; p.b = undefined ; console .log(p.a, p.b); console .log('c' in p, p.c);
RegExp 创建了一个正则表达式对象,用于将文本与一个模式匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var regex1 = /\w+/ ;var regex2 = new RegExp ('\\w+' );console .log(regex1); console .log(regex2); console .log(regex1 === regex2); var regex1 = RegExp ('foo*' ,'g' );var str1 = 'table football, foosball' ;let array1;while ((array1 = regex1.exec(str1)) !== null ) { console .log(`Found ${array1[0 ]} . Next starts at ${regex1.lastIndex} .` ); } var str = 'hello world!' ;var result = /^hello/ .test(str);console .log(result);
Map 保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。
本质上是键值对的集合,类似集合,可以遍历,方法很多可以跟各种数据格式转换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 var map1 = new Map ();map1.set('a' , 'alpha' ); map1.set('b' , 'beta' ); map1.set('g' , 'gamma' ); console .log(map1.size); map1.clear(); console .log(map1.size); map1.set('bar' , 'foo' ); console .log(map1.delete('bar' )); console .log(map1.has('bar' )); map1.set('0' , 'foo' ); map1.set(1 , 'bar' ); var iterator1 = map1.entries();console .log(iterator1.next().value); console .log(iterator1.next().value); function logMapElements (value, key, map ) { console .log("m[" + key + "] = " + value); } Map ([["foo" , 3 ], ["bar" , {}], ["baz" , undefined ]]).forEach(logMapElements);map1.set('bar' , 'foo' ); console .log(map1.get('bar' )); console .log(map1.get('baz' )); map1.set('0' , 'foo' ); map1.set(1 , 'bar' ); var iterator1 = map1.keys();console .log(iterator1.next().value); console .log(iterator1.next().value); var myMap = new Map ();myMap.set("0" , "foo" ); myMap.set(1 , "bar" ); myMap.set({}, "baz" ); var mapIter = myMap.values();console .log(mapIter.next().value); console .log(mapIter.next().value); console .log(mapIter.next().value); var map1 = new Map ();map1.set('0' , 'foo' ); map1.set(1 , 'bar' ); var iterator1 = map1[Symbol .iterator]();for (let item of iterator1) { console .log(item); }
Set 允许存储任何类型的唯一值,无论是原始值或者是对象引用。
成员唯一、无序且不重复。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 const set1 = new Set ([1 , 2 , 3 , 4 , 5 ]);console .log(set1.has(1 )); console .log(set1.has(5 )); console .log(set1.has(6 )); var mySet = new Set ();mySet.add(1 ); mySet.add(5 ).add("some text" ); console .log(mySet); mySet.add(5 ).add(1 ); console .log(mySet); var mySet = new Set ();mySet.add(1 ); mySet.add("foo" ); mySet.size; mySet.has("foo" ); mySet.clear(); mySet.size; mySet.has("bar" ) var mySet = new Set ();mySet.add("foo" ); mySet.delete("bar" ); mySet.delete("foo" ); mySet.has("foo" ); var mySet = new Set ();mySet.add("foobar" ); mySet.add(1 ); mySet.add("baz" ); var setIter = mySet.entries();console .log(setIter.next().value); console .log(setIter.next().value); console .log(setIter.next().value); function logSetElements (value1, value2, set ) { console .log("s[" + value1 + "] = " + value2); } new Set (["foo" , "bar" , undefined ]).forEach(logSetElements);var mySet = new Set ();mySet.add("foo" ); mySet.add("bar" ); mySet.add("baz" ); var setIter = mySet.values();console .log(setIter.next().value); console .log(setIter.next().value); console .log(setIter.next().value);
WeakSet 允许将弱保持对象存储在一个集合中。
成员都是对象
成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏。
与Set相比,WeakSet 只能是对象的集合,而不能是任何类型的任意值。
WeakSet持弱引用:集合中对象的引用为弱引用。 如果没有其他的对WeakSet中对象的引用,那么这些对象会被当成垃圾回收掉。 这也意味着WeakSet中没有存储当前对象的列表。 正因为这样,WeakSet 是不可枚举的。
1 2 3 4 5 6 7 8 9 10 11 var ws = new WeakSet ();var foo = {};var bar = {};ws.add(foo); ws.add(bar); ws.has(foo); ws.has(bar); ws.delete(foo); ws.has(foo); ws.has(bar); ws.clear();
WeakMap 是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
只接受对象作为键名(null除外),不接受其他类型的值作为键名。
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的。
WeakMap 的 key 只能是 Object 类型。 原始数据类型 是不能作为 key 的(比如 Symbol)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const wm1 = new WeakMap (), wm2 = new WeakMap (), wm3 = new WeakMap (); const o1 = {}, o2 = function ( ) {}, o3 = window ; wm1.set(o1, 37 ); wm1.set(o2, "azerty" ); wm2.set(o1, o2); wm2.set(o3, undefined ); wm2.set(wm1, wm2); wm1.get(o2); wm2.get(o2); wm2.get(o3); wm1.has(o2); wm2.has(o2); wm2.has(o3); wm3.set(o1, 37 ); wm3.get(o1); wm1.has(o1); wm1.delete(o1); wm1.has(o1);
isFinite 判断被传入的参数值是否为一个有限数值。
Number.isFinite() 与全局的 isFinite() 函数不同,全局的 isFinite() 会先把检测值转换为 Number ,然后在检测。
Number.isFinite() 不会将检测值转换为 Number对象,如果检测值不是 Number 类型,则返回 false。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 console .log(isFinite (1 )); console .log(isFinite (1.1 )); console .log(isFinite (1e1 )); console .log(isFinite (010 )); console .log(isFinite (0x10 )); console .log(isFinite ([])); console .log(isFinite ([1 ])); console .log(isFinite (['1' ])); console .log(isFinite (null )); console .log(isFinite (Infinity )); console .log(isFinite (NaN )); console .log(isFinite (undefined )); console .log(isFinite ({})); console .log(isFinite ('a' )); console .log(isFinite ('2a' )); console .log(isFinite ('1' )); console .log(Number .isFinite('1' ));
isNaN 检查其参数是否是非数字值。
1 2 3 4 5 6 7 8 9 10 11 12 13 console .log(isNaN (666 )); console .log(isNaN ("666" )); console .log(isNaN ("66b" )); console .log(isNaN (NaN )); console .log(isNaN (undefined )); console .log(isNaN ([26 ,1 ])); console .log(isNaN (Infinity )); console .log(isNaN (true )); console .log(isNaN ('2e7' )); console .log(isNaN ([])); console .log(isNaN ([26 ])); console .log(isNaN (['26' ])); console .log(isNaN (null ));
运算符 一元运算符 delete 1 2 3 4 5 var o = new Object ;o.name = "David" ; alert(o.name); delete o.name;alert(o.name);
void 前增量(++i)/前减量运算符(–i) 后增量(i++)/后减量运算符(i–) 一元加法和一元减法 1 2 3 4 var sNum = "20" ;alert(typeof sNum); var iNum = +sNum;alert(typeof iNum);
位运算符 NOT(~) 1 2 3 4 5 6 7 var iNum1 = 25 ; var iNum2 = ~iNum1; alert(iNum2); var iNum1 = 25 ;var iNum2 = -iNum1 -1 ;alert(iNum2);
AND(&) 1 2 var iResult = 25 & 3 ;alert(iResult);
1 2 3 4 25 = 0000 0000 0000 0000 0000 0000 0001 1001 3 = 0000 0000 0000 0000 0000 0000 0000 0011 --------------------------------------------- AND = 0000 0000 0000 0000 0000 0000 0000 0001
OR(|) 1 2 var iResult = 25 | 3 ;alert(iResult);
1 2 3 4 25 = 0000 0000 0000 0000 0000 0000 0001 1001 3 = 0000 0000 0000 0000 0000 0000 0000 0011 -------------------------------------------- OR = 0000 0000 0000 0000 0000 0000 0001 1011
XOR(^) 1 2 var iResult = 25 ^ 3 ;alert(iResult);
1 2 3 4 25 = 0000 0000 0000 0000 0000 0000 0001 1001 3 = 0000 0000 0000 0000 0000 0000 0000 0011 --------------------------------------------- XOR = 0000 0000 0000 0000 0000 0000 0001 1010
左移运算(<<) 1 2 var iOld = 2 ; var iNew = iOld << 5 ;
有符号右移运算(>>) 1 2 var iOld = 64 ; var iNew = iOld >> 5 ;
无符号右移运算(>>>) 1 2 3 4 5 var iOld = 64 ; var iNew = iOld >>> 5 ; var iUnsigned64 = -64 >>> 0 ; console .log(iUnsigned64.toString(2 )); console .log(-1 >>>31 );
运算操作 Infinity 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 console .log(Infinity *0 ); console .log(Infinity *Infinity ); console .log(Infinity *-Infinity ); console .log(0 /Infinity ); console .log(1 /0 ); console .log(Infinity /0 ); console .log(Infinity /Infinity ); console .log(-Infinity /0 ); console .log(Infinity %0 ); console .log(Infinity %Infinity ); console .log(0 %0 ); console .log(5 + "5" ); console .log("Blue" < "alpha" ); console .log("25" < "3" ); console .log("25" < 3 ); console .log("a" < 3 ); console .log("a" >= 3 ); console .log(null == undefined ); console .log("NaN" == NaN ); console .log(5 == NaN ); console .log(NaN == NaN ); console .log(NaN != NaN ); console .log(false == 0 ); console .log(true == 1 ); console .log(true == 2 ); console .log(undefined == 0 ); console .log(null == 0 ); console .log("5" == 5 ); console .log(true || unknown); console .log(false || unknown); console .log();console .log();console .log();console .log();console .log();
优先级
值
运算符
描述
实例
20
( )
表达式分组
(3 + 4)
19
.
成员
person.name
19
[]
成员
person[“name”]
19
()
函数调用
myFunction()
19
new
创建
new Date()
17
++
后缀递增
i++
17
–
后缀递减
i–
16
++
前缀递增
++i
16
–
前缀递减
–i
16
!
逻辑否
!(x==y)
16
typeof
类型
typeof x
15
**
求幂
(ES7) 10 ** 2
14
*
乘
10 * 5
14
/
除
10 / 5
14
%
模数除法
10 % 5
13
+
加
10 + 5
13
-
减
10 - 5
12
<<
左位移
x << 2
12
>>
右位移
x >> 2
12
>>>
右位移(无符号)
x >>> 2
11
<
小于
x < y
11
<=
小于或等于
x <= y
11
>
大于
x > y
11
>=
大于或等于
x >= y
11
in
对象中的属性
“PI” in Math
11
instanceof
对象的实例
instanceof Array
10
==
相等
x == y
10
===
严格相等
x === y
10
!=
不相等
x != y
10
!==
严格不相等
x !== y
9
&
按位与
x & y
8
^
按位
XOR x ^ y
7
|
按位或
x
6
&&
逻辑与
x && y
5
||
逻辑否
x
4
? :
条件
? “Yes” : “No”
3
=
赋值
x = y
3
+=
赋值
x += y
3
-=
赋值
x -= y
3
*=
赋值
x *= y
3
%=
赋值
x %= y
3
<<=
赋值
x <<= y
3
>>=
赋值
x >>= y
3
>>>=
赋值
x >>>= y
3
&=
赋值
x &= y
3
^=
赋值
x ^= y
3
|=
赋值
x
2
yield
暂停函数
yield x
1
,
逗号
7 , 8
语句/块 标签语句 break 语句和 continue 语句都可以与有标签的语句联合使用,返回代码中的特定位置。
1 2 3 4 5 6 7 8 9 10 11 var iNum = 0 ;outermost: for (var i=0 ; i<10 ; i++) { for (var j=0 ; j<10 ; j++) { if (i == 5 && j == 5 ) { break outermost; } iNum++; } } console .log(iNum);
1 2 3 4 5 6 7 8 9 10 11 var iNum = 0 ;outermost: for (var i=0 ; i<10 ; i++) { for (var j=0 ; j<10 ; j++) { if (i == 5 && j == 5 ) { continue outermost; } iNum++; } } console .log(iNum);
with 1 2 3 4 var sMessage = "hello" ;with (sMessage) { alert(toUpperCase()); }
闭包(closure) 指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。
对象 面向对象
封装 - 把相关的信息(无论数据或方法)存储在对象中的能力
聚集 - 把一个对象存储在另一个对象内的能力
继承 - 由另一个类(或多个类)得来类的属性和方法的能力
多态 - 编写能以多种方法运行的函数或方法的能力
对象类型 在 ECMAScript 中,所有对象并非同等创建的。
一般来说,可以创建并使用的对象有三种
本地对象 Object
、Function
、Array
、String
、Boolean
、Number
、Date
、RegExp
、Error
、EvalError
、RangeError
、ReferenceError
、SyntaxError
、TypeError
、URIError
内置对象 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。
宿主对象 所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。
所有 BOM 和 DOM 对象都是宿主对象。
原型 构造函数方式 1 2 3 4 5 6 7 8 9 10 function Car (sColor,iDoors,iMpg ) { this .color = sColor; this .doors = iDoors; this .mpg = iMpg; this .showColor = function ( ) { alert(this .color); }; } var oCar1 = new Car("red" ,4 ,23 );var oCar2 = new Car("blue" ,3 ,25 );
原型方式 1 2 3 4 5 6 7 8 9 10 11 function Car (sColor,iDoors,iMpg ) { this .color = sColor; this .doors = iDoors; this .mpg = iMpg; this .drivers = new Array ("Mike" ,"John" ); } Car.prototype.showColor = function ( ) { alert(this .color); }; var oCar1 = new Car("red" ,4 ,23 );var oCar2 = new Car("blue" ,3 ,25 );
动态原型方法 1 2 3 4 5 6 7 8 9 10 11 12 function Car (sColor,iDoors,iMpg ) { this .color = sColor; this .doors = iDoors; this .mpg = iMpg; this .drivers = new Array ("Mike" ,"John" ); if (typeof Car._initialized == "undefined" ) { Car.prototype.showColor = function ( ) { alert(this .color); }; Car._initialized = true ; } }
对象冒充 1 2 3 4 5 6 7 8 9 10 11 12 13 function ClassA (sColor ) { this .color = sColor; this .sayColor = function ( ) { alert(this .color); }; } function ClassB (sColor ) { ClassA.call(this , sColor); }
原型链 原型链的弊端是不支持多重继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 function ClassA ( ) {} ClassA.prototype.color = "blue" ; ClassA.prototype.sayColor = function ( ) { console .log(this .color); }; function ClassB ( ) {} ClassB.prototype = new ClassA(); ClassB.prototype.name = "" ; ClassB.prototype.sayName = function ( ) { console .log(this .name); }; var objA = new ClassA();var objB = new ClassB();objA.color = "blue" ; objB.color = "red" ; objB.name = "John" ; objA.sayColor(); objB.sayColor(); objB.sayName(); console .log(objB instanceof ClassA); console .log(objB instanceof ClassB); function fooA ( ) {}function fooB ( ) {}function fooC ( ) {}function fooD ( ) {}function fooE ( ) {}fooA.prototype.a = 1 ; fooB.prototype = Object .create(fooA.prototype); fooB.prototype.b = 2 ; fooC.prototype = Object .create(fooB.prototype); fooC.prototype.c = 3 ; fooD.prototype = Object .create(fooC.prototype); fooD.prototype.d = 4 ; fooE.prototype = Object .create(fooD.prototype); fooE.prototype.e = 5 ; var obj = new fooE();
__proto__
(不推荐)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var obj = { a: 1 , b: { c: 2 } } var fooA = function ( ) { this .__proto__ = obj; this .id = 123 ; } var fooB = function ( ) { this .name = 'ab' ; } fooB.prototype = new fooA(); console .log(new fooA());console .log(new fooB());
async、await Async 1 2 3 async function f ( ) { return 1 }
这个函数总是返回一个promise
,如果代码中有return <非promise>
语句,JavaScript会自动把返回的这个value值包装成promise
的resolved
值。
1 2 3 4 async function f ( ) { return 1 } f().then(alert)
也可以显式的返回一个promise
,这个将会是同样的结果:
1 2 3 4 async function f ( ) { return Promise .resolve(1 ) } f().then(alert)
Await 1 2 let value = await promise
await
可以让JavaScript进行等待,直到一个promise
执行并返回它的结果,JavaScript才会继续往下执行。
1 2 3 4 5 6 7 8 9 10 async function f ( ) { let promise = new Promise ((resolve, reject ) => { setTimeout(() => resolve('done!' ), 1000 ) }) let result = await promise; alert(result); } f()
await
字面上使得JavaScript等待,直到promise
处理完成,这并不会花费任何的cpu资源,因为引擎能够同时做其他工作:执行其他脚本,处理事件等等。
不能在常规函数里使用await
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 async function showAvatar ( ) { let response = await fetch('/article/promise-chaining/user.json' ); let user = await response.json(); let githubResponse = await fetch(`https://api.github.com/users/${user.name} ` ); let githubUser = await githubResponse.json(); let img = document .createElement('img' ); img.src = githubUser.avatar_url; img.className = 'promise-avatar-example' ; documenmt.body.append(img) await new Promise ((resolve, reject ) => { setTimeout(resolve, 3000 ) }); img.remove(); return githubUser; } showAvatar();
安全 CORS 跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器,让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。
当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
前文提到的由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求。
Web 字体 (CSS 中通过 @font-face 使用跨域字体资源), 因此,网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。
WebGL 贴图
使用 drawImage 将 Images/video 画面绘制到 canvas
新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。
在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
1 2 3 4 5 Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: content-type Access-Control-Allow-Origin: http://xxx.xxx # Access-Control-Allow-Origin: * Access-Control-Max-Age: 200
简单请求 请求不会触发 CORS 预检请求,这样的请求为“简单请求”
满足所有下述条件:
预检请求 必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。
附带身份凭证的请求 Fetch 与 CORS 的一个的特性是,可以基于 HTTP cookies 和 HTTP 认证信息发送身份凭证。
一般而言,对于跨域 XMLHttpRequest 或 Fetch 请求,浏览器不会发送身份凭证信息。
如果要发送凭证信息,需要设置 XMLHttpRequest 的某个特殊标志位。
1 2 3 4 5 6 7 8 9 10 11 var invocation = new XMLHttpRequest();var url = 'http://bar.other/resources/credentialed-content/' ;function callOtherDomain ( ) { if (invocation) { invocation.open('GET' , url, true ); invocation.withCredentials = true ; invocation.onreadystatechange = handler; invocation.send(); } }
如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。
对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。
将 Access-Control-Allow-Origin 的值设置为 http://foo.example,则请求将成功执行。
HTTP 响应首部字段 Access-Control-Allow-Origin 1 Access-Control-Allow-Origin: <origin> | *
指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。
服务端指定了具体的域名而非“*”,那么响应首部中的 Vary 字段的值必须包含 Origin。这将告诉客户端:服务器对不同的源站返回不同的内容。
1 2 3 4 5 6 7 8 9 Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header # 默认情况下,只有六种 simple response headers (简单响应首部)可以暴露给外部: # Cache-Control # Content-Language # Content-Type # Expires # Last-Modified # Pragma # 如果想要让客户端可以访问到其他的首部信息,可以将它们在 Access-Control-Expose-Headers 里面列出来。
让服务器把允许浏览器访问的头放入白名单,这样浏览器就能够通过getResponseHeader访问X-My-Custom-Header和 X-Another-Custom-Header 响应头了。
Access-Control-Max-Age 1 Access-Control-Max-Age: <delta-seconds>
指定了preflight请求的结果能够被缓存多久,delta-seconds 参数表示preflight请求的结果在多少秒内有效。
上限是24小时 (即86400秒),而在Chromium 中则是10分钟(即600秒)。Chromium 同时规定了一个默认值 5 秒。 如果值为 -1,则表示禁用缓存,每一次请求都需要提供预检请求,即用OPTIONS请求进行检测。
Access-Control-Allow-Credentials 1 Access-Control-Allow-Credentials: true
指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。
当用在对preflight预检测请求的响应中时,它指定了实际的请求是否可以使用credentials。
简单 GET 请求不会被预检;如果对此类请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。
Credentials可以是 cookies, authorization headers 或 TLS client certificates。
Access-Control-Allow-Methods 1 2 3 Access-Control-Allow-Methods: <method>[, <method>]* # 用逗号隔开的允许使用的 HTTP request methods 列表。 # Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
1 Access-Control-Allow-Headers: <field-name>[, <field-name>]*
用于预检请求的响应。其指明了实际请求中允许携带的首部字段。
以下这些特定的首部是一直允许的:Accept, Accept-Language, Content-Language, Content-Type (但只在其值属于 MIME 类型 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种时)。这些被称作simple headers,无需特意声明它们。
CSP HTTP 响应头Content-Security-Policy允许站点管理者控制用户代理能够为指定的页面加载哪些资源。
除了少数例外情况,设置的政策主要涉及指定服务器的源和脚本结束点。
这将帮助防止跨站脚本攻击(Cross-Site Script)(XSS)。
1 Content-Security-Policy: <policy-directive>; <policy-directive>
child-src
:为 web workers 和其他内嵌浏览器内容(例如用<frame>
和<iframe>
加载到页面的内容)定义合法的源地址。
如果开发者希望管控内嵌浏览器内容和 web worker 应分别使用frame-src和worker-src 指令,来相对的取代 child-src。
connect-src
:限制能通过脚本接口加载的URL。
default-src
:为其他取指令提供备用服务fetch directives。
font-src
:设置允许通过@font-face加载的字体源地址。
frame-src
: 设置允许通过类似<frame>
和<iframe>
标签加载的内嵌内容的源地址。
img-src
: 限制图片和图标的源地址
manifest-src
: 限制应用声明文件的源地址。
media-src
:限制通过<audio>
、<video>
或<track>
标签加载的媒体文件的源地址。
object-src
:限制<object>
、<embed>
、<applet>
标签的源地址。
被object-src控制的元素可能碰巧被当作遗留HTML元素,导致不支持新标准中的功能(例如<iframe>
中的安全属性sandbox和allow)。
因此建议限制该指令的使用(比如,如果可行,将object-src
显式设置为none
)。
prefetch-src
: 指定预加载或预渲染的允许源地址。
script-src
: 限制JavaScript的源地址。
style-src
: 限制层叠样式表文件源。
webrtc-src
: 指定WebRTC连接的合法源地址。
worker-src
: 限制Worker、SharedWorker或者ServiceWorker脚本源。
文档指令管理文档属性或者worker环境应用的策略。
base-uri
: 限制在DOM中<base>
元素可以使用的URL。
plugin-types
: 通过限制可以加载的资源类型来限制哪些插件可以被嵌入到文档中。
sandbox
: 类似<iframe>
sandbox属性,为请求的资源启用沙盒。
disown-opener
: 确保资源在导航的时候能够脱离父页面。(windown.opener 对象)
导航指令管理用户能打开的链接或者表单可提交的链接
form-action
: 限制能被用来作为给定上下文的表单提交的目标 URL(说白了,就是限制 form 的 action 属性的链接地址)
frame-ancestors
: 指定可能嵌入页面的有效父项<frame>
, <iframe>
, <object>
, <embed>
, or <applet>
navigation-to
: 限制文档可以通过以下任何方式访问URL (a, form, window.location, window.open, etc.)
报告指令控制 CSP 违规的报告过程. 更多请看 Content-Security-Policy-Report-Only 报头.
report-uri 当出现可能违反CSP的操作时,让客户端提交报告。这些违规报告会以JSON文件的格式通过POST请求发送到指定的URI report-to Fires a SecurityPolicyViolationEvent.
其他指令 | Other directives
block-all-mixed-content
: 当使用HTTPS加载页面时阻止使用HTTP加载任何资源。
referrer
: 用来指定会离开当前页面的跳转链接的 referer header 信息。应该使用 Referrer-Policy 替代。
require-sri-for
: 需要使用 SRI 作用于页面上的脚本或样式。
upgrade-insecure-requests
: 让浏览器把一个网站所有的不安全 URL(通过 HTTP 访问)当做已经被安全的 URL 链接(通过 HTTPS 访问)替代。这个指令是为了哪些有量大不安全的传统 URL 需要被重写时候准备的。
多内容安全策略 CSP 允许在一个资源中指定多个策略, 包括通过 Content-Security-Policy 头, 以及 Content-Security-Policy-Report-Only 头,和 组件。
1 2 Content-Security-Policy: default-src 'self' http://example.com; connect-src 'none'; Content-Security-Policy: connect-src http://example.com/; script-src http://example.com/
尽管第二个策略允许连接, 第一个策略仍然包括了 connect-src ‘none’。
添加了附加的策略后,只会让资源保护的能力更强,也就是说不会有接口可以被允许访问,等同于最严格的策略,connect-src ‘none’ 强制开启。
示例 禁用不安全的内联/动态执行, 只允许通过 https加载这些资源 (images, fonts, scripts, etc.)
1 2 3 4 5 Content-Security-Policy: default -src https: <meta http-equiv="Content-Security-Policy" content="default-src https:" >
已经存在的一个网站,用了太多内联代码修复问题,而且想确保资源只从 https 加载,并且禁止插件:
1 Content-Security-Policy: default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'
还没有开始实施上面的策略;相反,只是开始上报可能会发生违反安全策略的行为:
1 Content-Security-Policy-Report-Only: default-src https:; report-uri /csp-violation-report-endpoint/
XSS 跨站脚本攻击Cross-site scripting (XSS)是一种安全漏洞,攻击者可以利用这种漏洞在网站上注入恶意的客户端代码。当被攻击者登陆网站时就会自动运行这些恶意代码,从而,攻击者可以突破网站的访问权限,冒充受害者。
如果Web应用程序没有部署足够的安全验证,那么,这些攻击很容易成功。浏览器无法探测到这些恶意脚本是不可信的,所以,这些脚本可以任意读取cookie,session tokens,或者其它敏感的网站信息,或者让恶意脚本重写HTML内容。
在以下2种情况下,容易发生XSS攻击:1)数据从一个不可靠的链接进入到一个web应用程序。2)没有过滤掉恶意代码的动态内容被发送给web用户。
恶意内容一般包括 JavaScript,但是,有时候也会包括HTML,FLASH。XSS攻击的形式千差万别,但是,它们的共同点为:将一些隐私数据像cookie、session发送给攻击者,将受害者重定向到一个由攻击者控制的网站,在受害者的机器上进行一些恶意操作。
XSS攻击可以分为3类:存储型(持久型)、反射型(非持久型)、基于DOM。
技巧/解答 call/apply bind 不使用 call/apply 实现 bind
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 if (!Function .prototype.bind2) (function ( ) { var slice = Array .prototype.slice; Function .prototype.bind2 = function ( ) { var thatFunc = this , thatArg = arguments [0 ]; var args = []; if (arguments && arguments .length>1 ){ for (var index in arguments ){ if (index=='0' ){ continue ; } args.push(arguments [index]); } } if (typeof thatFunc !== 'function' ) { throw new TypeError ('Function.prototype.bind - what is trying to be bound is not callable' ); } return function ( ) { var args2 = []; if (arguments && arguments .length>0 ){ for (var index in arguments ){ args2.push(arguments [index]); } } var funcArgs = args.concat(args2); if (thatArg){ var symbol = Symbol (); thatArg[symbol] = thatFunc; thatArg[symbol](...funcArgs); } else { thatFunc(...funcArgs); } }; }; })(); function foo (age ) { console .log(this .name + ': ' + age); } foo.bind2({name :'123' })(20 );
uncurrying 1 2 3 4 5 6 7 8 9 10 11 12 var obj = { "length" : 1 , "0" : 1 } Function .prototype.uncurrying = function ( ) { var self = this ; return function ( ) { return Function .prototype.call.apply(self, arguments ); } } var push = Array .prototype.push.uncurrying()push(obj, 2 )
高阶函数 接受一个或多个函数作为输入
函数的柯里化
接收函数A作为参数,运行后能够返回一个新的函数。并且这个新的函数能够处理函数A的剩余参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function createCurry (func, args ) { var arity = func.length; var args = args || []; return function ( ) { var _args = [].slice.call(arguments ); [].push.apply(_args, args); if (_args.length < arity) { return createCurry.call(this , func, _args); } return func.apply(this , _args); } }
字符串 StringBuffer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function StringBuffer ( ) { this ._strings_ = new Array (); } StringBuffer.prototype.append = function (str ) { this ._strings_.push(str); }; StringBuffer.prototype.toString = function ( ) { return this ._strings_.join("" ); }; var buffer = new StringBuffer ();buffer.append("hello " ); buffer.append("world" ); var result = buffer.toString();
解疑 setTimeout、Promise、Async/Await 的区别 事件循环分两种情况的,即宏任务(macrotask)和微任务(microtask)。
当主线程任务完成为空去Event Quenu读取函数的时候,是先读取的微任务,当微任务执行完毕之后,才会继续执行宏任务。
执行顺序
解答 将数组扁平化去并除其中重复部分数据 1 2 var arr = [ [1 , 2 , 2 ], [3 , 4 , 5 , 5 ], [6 , 7 , 8 , 9 , [11 , 12 , [12 , 13 , [14 ] ] ] ], 10 ];Array .from(new Set (arr.flat(Infinity ))).sort((a,b )=> { return a-b})
简单讲解一下http2的多路复用 HTTP2采用二进制格式传输,取代了HTTP1.x的文本格式,二进制格式解析更高效。 多路复用代替了HTTP1.x的序列和阻塞机制,所有的相同域名请求都通过同一个TCP连接并发完成。在HTTP1.x中,并发多个请求需要多个TCP连接,浏览器为了控制资源会有6-8个TCP连接都限制。 HTTP2中
同域名下所有通信都在单个连接上完成,消除了因多个 TCP 连接而带来的延时和内存消耗。 单个连接上可以并行交错的请求和响应,之间互不干扰
对TCP三次握手和四次挥手的理解 三次握手: 1、客户端发送syn包到服务器,等待服务器确认接收。 2、服务器确认接收syn包并确认客户的syn,并发送回来一个syn+ack的包给客户端。 3、客户端确认接收服务器的syn+ack包,并向服务器发送确认包ack,二者相互建立联系后,完成tcp三次握手。 四次挥手:中间多了一层 等待服务器再一次响应回复相关数据的过程
深度优先遍历和广度优先遍历 递归、队列
用深度优先思想和广度优先思想实现一个拷贝函数
浏览器 AJAX AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML) 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
创建 XMLHttpRequest 对象
设置 onreadystatechange 状态更改事件
调用对象的 open 方法并规定请求类型(GET、POST、PUT、DELETE、HEAD、OPTIONS)、URL以及是否异步处理请求 async(true-异步|false-同步)
可选,setRequestHeader 方法设置请求头
调用对象的 send 方法将请求发送到服务器,当请求类型为 POST、PUT 时,可发送请求数据 send(string)
请求头 content-type
1 Content-Type: application/x-www-form-urlencoded; charset=UTF-8
application/x-www-form-urlencoded
: Form Data
multipart/form-data
: Request Payload,可上传文件
application/json
: Request Payload
text/xml
: Request Payload
charset=UTF-8
请求类型比较 所有HTTP请求都是这种格式:HTTP请求头+HTTP请求体。
1 2 3 4 5 6 7 <method> <url> HTTP/1.1 <header1>: <headerValue1> <header2>: <headerValue2> ... <headerN>: <headerValueN> <body data...>
POST
: 一般只用于新增数据
GET
: chrome浏览器限制请求URL长度20000+个字符 一般只用于获取数据
PUT
: 只用于更新数据
DELETE
: 只用于删除数据
HEAD
: 只用于头请求
OPTIONS
: 只用于检测是否可访问,用于跨域检测
方法/事件
getAllResponseHeaders
: 检索资源(文件)的头信息
onreadystatechange
: 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
readyState:
0
: 请求未初始化
1
: 服务器连接已建立
2
: 请求已接收
3
: 请求处理中
4
: 请求已完成,且响应已就绪
status:
1xx
: 信息响应类,表示接收到请求并且继续处理
2xx
: 处理成功响应类,表示动作被成功接收、理解和接受
3xx
: 重定向响应类,为了完成指定的动作,必须接受进一步处理
4xx
: 客户端错误,客户请求包含语法错误或者是不能正确执行
5xx
: 服务端错误,服务器不能正确执行一个正确的请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 100——客户必须继续发出请求 101——客户要求服务器根据请求转换HTTP协议版本 200——"OK"交易成功 201——提示知道新文件的URL 202——接受和处理、但处理未完成 203——返回信息不确定或不完整 204——请求收到,但返回信息为空 205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件 206——服务器已经完成了部分用户的GET请求 300——请求的资源可在多处得到 301——删除请求数据 302——在其他地址发现了请求数据 303——建议客户访问其他URL或访问方式 304——客户端已经执行了GET,但文件未变化 305——请求的资源必须从服务器指定的地址得到 306——前一版本HTTP中使用的代码,现行版本中不再使用 307——申明请求的资源临时性删除 400——错误请求,如语法错误 401——请求授权失败 402——保留有效ChargeTo头响应 403——请求不允许 404——没有发现文件、查询或URl 405——用户在Request-Line字段定义的方法不允许 406——根据用户发送的Accept拖,请求资源不可访问 407——类似401,用户必须首先在代理服务器上得到授权 408——客户端没有在用户指定的饿时间内完成请求 409——对当前资源状态,请求不能完成 410——服务器上不再有此资源且无进一步的参考地址 411——服务器拒绝用户定义的Content-Length属性请求 412——一个或多个请求头字段在当前请求中错误 413——请求的资源大于服务器允许的大小 414——请求的资源URL长于服务器允许的长度 415——请求资源不支持请求项目格式 416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含If-Range请求头字段 417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求 500——服务器产生内部错误 501——服务器不支持请求的函数 502——服务器暂时不可用,有时是为了防止发生系统过载 503——服务器过载或暂停维修 504——关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长 505——服务器不支持或拒绝支请求头中指定的HTTP版本
返回结果
responseText
: 获得字符串形式的响应数据。
responseXML
: 获得 XML 形式的响应数据。
例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var xmlhttp;if (window .XMLHttpRequest) { xmlhttp=new XMLHttpRequest(); } else { xmlhttp=new ActiveXObject("Microsoft.XMLHTTP" ); } xmlhttp.onreadystatechange=function ( ) { if (xmlhttp.readyState==4 && xmlhttp.status==200 ) { console .log(xmlhttp.responseText); } } xmlhttp.open("GET" ,"/try/ajax/ajax_info.txt" ,true ); xmlhttp.setRequestHeader('Content-Type' , 'application/x-www-form-urlencoded' ); xmlhttp.send();
事件处理
addEventListener
: 添加监听事件
dispatchEvent
: 触发事件
removeEventListener
: 移除监听事件
元素操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var newAttr = document .createAttribute(strAttrName);newAttr.value=strAttrValue; ele.attributes.setNamedItem(newAttr); return HTMLElement.prototype.isPrototypeOf(anyTarget);a.replace(/[<>&"]/g ,function (c ) {return {'<' :'<' ,'>' :'>' ,'&' :'&' ,'"' :'"' }[c];}) var objBolb = new Blob(['\ufeff' +content],{type :'text/plain,charset=UTF-8' });var eleLink = document .createElement('a' );eleLink.target = '_blank' ; eleLink.href = URL.createObjectURL(objBolb); eleLink.download = name; document .body.appendChild(eleLink);eleLink.click(); eleLink.remove(); window .getSelection().removeAllRanges();var range = document .createRange();range.selectNode(objTarget); window .getSelection().addRange(range);successFul = document .execCommand('copy' );
A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态
服务器不重启,客户继续工作,就会发现对方没有回应(ETIMEOUT),路由器聪明的话,则是目的地不可达(EHOSTUNREACH)。
服务器重启后,客户继续工作,然而服务器已丢失客户信息,收到客户数据后响应RST。
Node 介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块? 发出npm install命令,查询node_modules目录之中是否已经存在指定模块,若存在,不再重新安装; 若不存在,npm 向 registry 查询模块压缩包的网址,下载压缩包,存放在根目录下的.npm目录里,解压压缩包到当前项目的node_modules目录。
React React 中 setState 什么时候是同步的,什么时候是异步的? 如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。
指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。
在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this.state,但是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state。
这里所说的同步异步,并不是真正的同步异步,它还是同步执行的。
这里的异步指的是多个state会合成到一起进行批量更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Example extends React .Component { constructor () { super (); this .state = { val: 0 }; } componentDidMount() { this .setState({val : this .state.val + 1 }); console .log(this .state.val); this .setState({val : this .state.val + 1 }); console .log(this .state.val); setTimeout(() => { this .setState({val : this .state.val + 1 }); console .log(this .state.val); this .setState({val : this .state.val + 1 }); console .log(this .state.val); }, 0 ); } render() { return null ; } }