---
title: "JavaScript 筆記"
path: "JavaScript 筆記"
---
{%hackmd @RintarouTW/About %}
# JavaScript 筆記
[ECMAScript 262](https://tc39.es/ecma262/)
實作 JavaScript 的如 Browser, Node.js 稱為 Host。
# Object Model
Prototype Chain
<img src="https://i.imgur.com/bqeKvuD.png" style="filter:invert(.9)"></img>
$CF$ : Constructor Function
$CF_p$ : Constructor Function.prototype
$cf_{1-5}$ = new CF();
$cf_{1-5}$ : Object instance.prototype = $CF_p$
CFP1 is shared by $cf_{1-5}$
There is no implicit prototype link between CF and CFp.
In a class-based object-oriented language, in general, state is carried by instances, methods are carried by classes, and inheritance is only of structure and behaviour. In ECMAScript, the state and methods are carried by objects, while structure, behaviour, and state are all inherited.
Beginning with ECMAScript 2015, the ECMAScript language includes syntactic class definitions that permit programmers to concisely define objects that conform to the same class-like abstraction pattern used by the built-in objects.
## Object.assign()
Creates a new object by copying the values of all enumerable own properties from one or more source objects to a target object.
> Syntax: Object.assign(target, ...sources)
只 copy value, 所以 reference 也照樣當成 value copy 下來,但並不會深入把 reference 到的物件值也 copy 下來,prototype 與非 enumerable 的 property 也都不管。
所以當 property value 是 getter/setter/object 這些非 primitive type data 時,都只是 reference 回原來物件所指到的位置而已。
```javascript
let obj1 = { a: 0 , b: { c: 0}}; // b is only a reference
let obj2 = Object.assign({}, obj1);
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { "a": 1, "b": { "c": 3}}
console.log(JSON.stringify(obj2)); // { "a": 2, "b": { "c": 3}}
// Merge object
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
Object.assign(o1, o2, o3);
console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed.
// Merging objects with same properties
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };
const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
// Properties on the prototype chain and non-enumerable properties cannot be copied
const obj = Object.create({ foo: 1 }, { // foo is on obj's prototype chain.
bar: {
value: 2 // bar is a non-enumerable property.
},
baz: {
value: 3,
enumerable: true // baz is an own enumerable property.
}
});
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
```
## Object.create()
Creates a new object with the specified prototype object and properties.
> Syntax : Object.create(proto, [propertiesObject])
建立一個以傳入的 proto object 為 prototype 的 object,並把 propertiesObject 可 enumerable 的 property 一併指給新建的 object.
新建好傳回的 object.prototype 就是 proto 的值(proto 本身其實也只是一個 object reference), 而非 copy proto 的內容。
用中文說真的很像繞口令,實際上只要了解 prototype chain,其實並沒那麼複雜的。 (很多老 framework 都是這麼做的)
```javascript
const person = {
isHuman: false,
printIntroduction: function () {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create(person);
me.name = "Matthew"; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"
```
Javascript 以 prototype chain 實作繼承的方式。
```javascript
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
//If you don't set Rectangle.prototype.constructor to Rectangle,
//it will take the prototype.constructor of Shape (parent).
//To avoid that, we set the prototype.constructor to Rectangle (child).
Rectangle.prototype.constructor = Rectangle;
```
## Object.defineProperties()
Adds the named properties described by the given descriptors to an object.
> Syntax: Object.defineProperties(obj, props)
props
An object whose keys represent the names of properties to be defined or modified and whose values are objects describing those properties. Each value in props must be either a data descriptor or an accessor descriptor;
Data descriptors and accessor descriptors may optionally contain the following keys:
```
configurable
true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.
Defaults to false.
enumerable
true if and only if this property shows up during enumeration of the properties on the corresponding object.
Defaults to false.
```
A data descriptor also has the following optional keys:
```
value
The value associated with the property. Can be any valid JavaScript value (number, object, function, etc).
Defaults to undefined.
writable
true if and only if the value associated with the property may be changed with an assignment operator.
Defaults to false.
```
An accessor descriptor also has the following optional keys:
### Setter/Getter
```
get
A function which serves as a getter for the property, or undefined if there is no getter. The function's return value will be used as the value of the property.
Defaults to undefined.
set
A function which serves as a setter for the property, or undefined if there is no setter. The function will receive as its only argument the new value being assigned to the property.
Defaults to undefined.
```
```javascript
var obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
// etc. etc.
});
```
**configurable**, **writable**, **enumerable** 在 ECMAScript Specification 中稱為 Property Attributes.
## Object.defineProperty()
Adds the named property described by a given descriptor to an object.
> Syntax: Object.defineProperty(obj, prop, descriptor)
prop 是一個 property name:String 或 Symbol
descriptor 是 data descriptor 或 accessor descriptor。
## Object.keys() & Object.values() & Object.entries()
keys(), values(), entries() 都只會傳回 Enumerable 的 property。
```javascript
for (let [key, value] of Object.entries(object1)) {
console.log(`${key}: ${value}`);
}
```
## Object.getOwnPropertyNames() & Object.getOwnPropertySymbols()
不管 Enumerable 還是 non-Enumerable 都會傳回來。
## Object.getPrototypeOf()
```javascript
var proto = {};
var obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true
```
從表面上看起來像廢話,但這是實作 Object Model 裡重要的一件事,新建的 object.prototype 就是直接 reference 到同一個 proto,而非 copy。
## Object.fromEntries()
> Syntax : Object.fromEntries(iterable);
```javascript
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj);
// expected output: Object { foo: "bar", baz: 42 }
// Object Transformation
const object1 = { a: 1, b: 2, c: 3 };
const object2 = Object.fromEntries(
Object.entries(object1)
.map(([ key, val ]) => [ key, val * 2 ])
);
console.log(object2);
// { a: 2, b: 4, c: 6 }
```
# Function Object
最上層有個唯一叫做 **Function** 的 object,它也是繼承自 Object,而其它所有非 native function 的 javascript function 都是繼承這最上層的 **Function** 而來,實際上最上層的 **Function** 才有真正 Constructor allocate memory 的能力,我們所寫的 function 都是透過它 allocate 好 memory 後,才能當作 constructor 被人呼叫。
其實所有 function 都可被當成 class constructor 來建立新的 object instance。
### Function.prototype.apply(thisArg [, argsArray])
Calls a function and sets its this to the provided thisArg. Arguments can be passed as an Array object.
```javascript
// Using apply to append an array to another
const array = ['a', 'b'];
const elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]
// Using apply and built-in functions
// min/max number in an array
const numbers = [5, 6, 2, 3, 7];
// using Math.min/Math.max apply
let max = Math.max.apply(null, numbers);
// This about equal to Math.max(numbers[0], ...)
// or Math.max(5, 6, ...)
let min = Math.min.apply(null, numbers);
```
### Function.prototype.call()
Calls a function and sets its this to the provided value. Arguments can be passed as they are.
call() provides a new value of this to the function/method. With call(), you can write a method once and then inherit it in another object, without having to rewrite the method for the new object.
```javascript
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('feta', 5);
const fun = new Toy('robot', 40);
// Using call to invoke an anonymous function
const animals = [
{ species: 'Lion', name: 'King' },
{ species: 'Whale', name: 'Fail' }
];
for (let i = 0; i < animals.length; i++) {
(function(i) {
this.print = function() {
console.log('#' + i + ' ' + this.species
+ ': ' + this.name);
}
this.print();
}).call(animals[i], i);
}
// Using call to invoke a function and specifying the context for 'this'
function greet() {
const reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
console.log(reply);
}
const obj = {
animal: 'cats', sleepDuration: '12 and 16 hours'
};
greet.call(obj); // cats typically sleep between 12 and 16 hours
```
### Function.prototype.bind(thisArg[, arg1[, arg2[, ...argN]]])
Creates a new function which, when called, has its this set to the provided thisArg. Optionally, a given sequence of arguments will be prepended to arguments provided the newly-bound function is called.
```javascript
const module = {
x: 42,
getX: function() {
return this.x;
}
}
const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined
const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42
```
## Arrow Function
no this, arguments, etc...but very clean and useful.
依實作層次上看,Arrow Function 更接近傳統 C/C++ 裡的 function,而非 method。也不用考慮 excution context 裡的 this 是否需要重新 bind 的問題。
```javascript
var elements = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];
elements.map(element => element.length); // [8, 6, 7, 9]
elements.map(({ length }) => length); // [8, 6, 7, 9]
// arrow function has no it's own this, so this would be
// the one who defined the arrow function.
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| 適切的參考了Person建構式所建立的物件
}, 1000);
}
var p = new Person();
// Function Body
var func = x => x * x;
// concise 語法會內建 "return"
var func = () => ({foo: 1});
// return object need (), or {} would be seen as a block.
var simple = a => a > 15 ? 15 : a;
let max = (a, b) => a > b ? a : b;
// Easy array filtering, mapping, ...
var arr = [5, 6, 13, 0, 1, 18, 23];
var double = arr.map(v => v * 2); // [10, 12, 26, 0, 2, 36, 46]
var sum = arr.reduce((a, b) => a + b); // 66
var even = arr.filter(v => v % 2 == 0); // [6, 0, 18]
// 也支援 parameter list 的解構與 default value
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6
```
## Method
### Generator
```javascript
function* generator(i) {
yield i;
yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value);
// expected output: 10
console.log(gen.next().value);
// expected output: 20
// a generator function
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
```
Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.
Generators in JavaScript -- especially when combined with Promises -- are a very powerful tool for asynchronous programming as they mitigate -- if not entirely eliminate -- the problems with callbacks, such as Callback Hell and Inversion of Control. However, an even simpler solution to these problems can be achieved with async functions.
Calling a generator function does not execute its body immediately; an iterator object for the function is returned instead. When the iterator's next() method is called, the generator function's body is executed until the first yield expression, which specifies the value to be returned from the iterator or, with yield*, delegates to another generator function. The next() method returns an object with a value property containing the yielded value and a done property which indicates whether the generator has yielded its last value, as a boolean. Calling the next() method with an argument will resume the generator function execution, replacing the yield expression where execution was paused with the argument from next().
A return statement in a generator, when executed, will make the generator finish (i.e. the done property of the object returned by it will be set to true). If a value is returned, it will be set as the value property of the object returned by the generator.
Much like a return statement, an error thrown inside the generator will make the generator finished -- unless caught within the generator's body.
When a generator is finished, subsequent next() calls will not execute any of that generator's code, they will just return an object of this form: {value: undefined, done: true}.
```javascript
// Generator as an object property
const someObj = {
*generator () {
yield 'a';
yield 'b';
}
}
const gen = someObj.generator()
console.log(gen.next()); // { value: 'a', done: false }
console.log(gen.next()); // { value: 'b', done: false }
console.log(gen.next()); // { value: undefined, done: true }
// Generator as an object method
class Foo {
*generator () {
yield 1;
yield 2;
yield 3;
}
}
const f = new Foo ();
const gen = f.generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
// Generator as a computed property
class Foo {
*[Symbol.iterator] () {
yield 1;
yield 2;
}
}
const SomeObj = {
*[Symbol.iterator] () {
yield 'a';
yield 'b';
}
}
console.log(Array.from(new Foo)); // [ 1, 2 ]
console.log(Array.from(SomeObj)); // [ 'a', 'b' ]
```
## async & await
Asynchronous functions(AsyncFunction) operate in a separate order than the rest of the code via the event loop, returning an **implicit Promise** as its result. But the syntax and structure of code using async functions looks like standard synchronous functions.
```javascript
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await resolveAfter2Seconds();
console.log(result);
// expected output: 'resolved'
}
asyncCall();
```
While the async function is paused, the calling function continues running (having received the implicit Promise returned by the async function).
The purpose of **async/await** is to simplify using promises synchronously, and to perform some behavior on a group of Promises. As Promises are similar to structured callbacks, **async/await** is similar to combining generators and promises.
```javascript
(async () => {
// Default Configuration
const options = await import("./default.js");
setup(options);
})();
async function sequentialStart() {
console.log('==SEQUENTIAL START==')
// 1. Execution gets here almost instantly
const slow = await resolveAfter2Seconds()
console.log(slow) // 2. this runs 2 seconds after 1.
const fast = await resolveAfter1Second()
console.log(fast) // 3. this runs 3 seconds after 1.
}
async function concurrentStart() {
console.log('==CONCURRENT START with await==');
const slow = resolveAfter2Seconds() // starts timer immediately
const fast = resolveAfter1Second() // starts timer immediately
// 1. Execution gets here almost instantly
console.log(await slow) // 2. this runs 2 seconds after 1.
console.log(await fast) // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
}
function concurrentPromise() {
console.log('==CONCURRENT START with Promise.all==')
return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then((messages) => {
console.log(messages[0]) // slow
console.log(messages[1]) // fast
})
}
async function parallel() {
console.log('==PARALLEL with await Promise.all==')
// Start 2 "jobs" in parallel and wait for both of them to complete
await Promise.all([
(async()=>console.log(await resolveAfter2Seconds()))(),
(async()=>console.log(await resolveAfter1Second()))()
])
}
```
Rewriting a Promise chain with an async function
```javascript
function getProcessedData(url) {
return downloadData(url) // returns a promise
.catch(e => {
return downloadFallbackData(url) // returns a promise
})
.then(v => {
return processDataInWorker(v) // returns a promise
})
}
async function getProcessedData(url) {
let v
try {
v = await downloadData(url)
} catch(e) {
v = await downloadFallbackData(url)
}
return processDataInWorker(v)
}
```
## Promise (承諾)
```javascript
const promise1 = new Promise(function(resolve, reject) {
resolve('Success!');
});
promise1.then(function(value) {
console.log(value);
// expected output: "Success!"
});
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10*1000).then(() => saySomething("10 seconds")).catch(failureCallback);
```
## Method Define
更簡明的寫法,不用再打 function 一長串的字了。
Syntax:
```
const obj = {
get property() {},
set property(value) {},
property( parameters… ) {},
*generator( parameters… ) {},
async property( parameters… ) {},
async* generator( parameters… ) {},
// with computed keys
get [property]() {},
set [property](value) {},
[property]( parameters… ) {},
*[generator]( parameters… ) {},
async [property]( parameters… ) {},
async* [generator]( parameters… ) {},
};
```
Examples:
```javascript
const obj = {
foo() {
// ...
},
bar() {
// ...
}
}
// generator method using shorthand syntax
const obj2 = {
* g() {
let index = 0
while (true) {
yield index++
}
}
};
// async method
const obj3 = {
async f() {
await some_promise
}
}
// Computed property names
const bar = {
foo0: function() { return 0 },
foo1() { return 1 },
['foo' + 2]() { return 2 }
}
console.log(bar.foo2()) // 2
// A global function
function foo() {
return 1
}
let name = 'foo'
console.log(window[name]()) // 1
```
# Module
Module 有自己的 namespace, 才不會與其它 .js 產生 naming polution 的問題。
customElement 的 .js 也要用 type=module 來 load。
## import
```
import defaultExport from "module-name";
import * as name from "module-name";
import { export } from "module-name";
import { export as alias } from "module-name";
import { export1 , export2 } from "module-name";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";
```
```javascript
import * as myModule from '/modules/my-module.js';
myModule.doAllTheAmazingThings();
import {foo, bar} from '/modules/my-module.js';
import {
reallyReallyLongModuleExportName as shortName,
anotherLongModuleName as short
} from '/modules/my-module.js';
import '/modules/my-module.js';
import myDefault from '/modules/my-module.js';
// myModule used as a namespace
import myDefault, * as myModule from '/modules/my-module.js';
import('/modules/my-module.js')
.then((module) => {
// 在模塊內作點事情
})
.catch(err => {
// error handling
});
let module = await import('/modules/my-module.js');
```
## export
1. Named Exports (Zero or more exports per module)
2. Default Exports (One per module)
```
// Exporting individual features
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function functionName(){...}
export class ClassName {...}
// Export list
export { name1, name2, …, nameN };
// Renaming exports
export { variable1 as name1, variable2 as name2, …, nameN };
// Exporting destructured assignments with renaming
export const { name1, name2: bar } = o;
// Default exports
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
// Aggregating modules
export * from …; // does not set the default export
export * as name1 from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default } from …;
```
```javascript
// named exports
// 前面已經宣告的函式可以這樣輸出
export { myFunction };
// 輸出常數
export const foo = Math.sqrt(2);
// default export
export default function() {}
```
# Event Model
## Event Target & Dispatch
> EventTarget.addEventListener()
> EventTarget.removeEventListener()
> EventTarget.dispatchEvent()
## Event & CustomEvent
https://developer.mozilla.org/en-US/docs/Web/API/Event
```javascript
document.addEventListener("UpdateOptions", (evt) => {
console.log(evt.detail);
});
let evt = new CustomEvent("UpdateOptions", { detail: data });
document.dispatchEvent(evt);
```
## Event Passing (bubbling/capture)
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture
# Data Model
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
透過 Destructor Assignment 其實能加強 JavaScript 缺少 Type 描述的問題。
比如:
```javascript
function show(obj) {
console.log(obj.name);
console.log(obj.value);
}
// 改寫成
function show({name, value}) {
console.log(name);
console.log(value);
}
// array destructor
for (let [key, value] of Object.entries(obj)) {
console.log(key, " : " , value);
}
```
對於程式參數需求的更明確,閱讀性也更好。
for in 和 for of
```javascript
for (let prop in obj) {...}
for (let value of iterable) {...}
```
## JSON, Array, String
無需多言。
## ArrayBuffer + DataView
簡而言之 ArrayBuffer 就是 binary stream,而 DataView 就是 parser。
DataView 命名成 View 其實也很貼切,對於寫過 parser 的人來說,的確所有的 Stream 都是一串資料流,端看你如何去「看待與解釋」它。
Parser 更像是在這個 Stream 上把所認得的資料都一一加上 Tag,上層的 App 只是在針對 Tag 運作,不需要管究竟 Stream 長成什麼樣,又在哪。DataView 也就等同於 Tag 的功能。
## Browser Specific
### Local Storage
### Cookie
### Service Worker
Service workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available). They are intended, among other things, to enable the creation of effective offline experiences, intercept network requests and take appropriate action based on whether the network is available, and update assets residing on the server. They will also allow access to push notifications and background sync APIs.
A service worker is run in a worker context: it therefore has no DOM access, and runs on a different thread to the main JavaScript that powers your app, so it is not blocking. It is designed to be fully async; as a consequence, APIs such as synchronous XHR and localStorage can't be used inside a service worker.
https://github.com/mdn/sw-test
# Thread Model
# Document Object Model
```graphviz
digraph {
graph [bgcolor=transparent];
node[fontcolor="#888888";color="#888888"];
edge [color="#888800"];
rankdir=BT
"Element" -> "Node" -> "EventTarget"
}
```
EventTarget
- addListener()
- removeListener()
- dispatchEvent()
Element
- HTMLElement
- SVGElement
- ...
Node : Graph Node
- parent/child/sibling
- relation management
| Node Type | Value |
|-----------|-------|
|ELEMENT_NODE | 1|
|ATTRIBUTE_NODE | 2|
|TEXT_NODE | 3|
|CDATA_SECTION_NODE | 4|
|ENTITY_REFERENCE_NODE | 5|
|ENTITY_NODE | 6|
|PROCESSING_INSTRUCTION_NODE| 7|
|COMMENT_NODE| 8|
|DOCUMENT_NODE| 9|
|DOCUMENT_TYPE_NODE| 10|
|DOCUMENT_FRAGMENT_NODE| 11|
|NOTATION_NODE| 12|
## Shadow DOM
An important aspect of web components is encapsulation — being able to keep the markup structure, style, and behavior hidden and separate from other code on the page so that different parts do not clash, and the code can be kept nice and clean. The Shadow DOM API is a key part of this, providing a way to attach a hidden separated DOM to an element. This article covers the basics of using the Shadow DOM.
供 customeElements 使用
<img src="https://i.imgur.com/ZrzfPRo.png" style="filter:invert(.9)"></img>
Shadow DOM allows hidden DOM trees to be attached to elements in the regular DOM tree — this shadow DOM tree starts with a shadow root, underneath which can be attached to any elements you want, in the same way as the normal DOM.
- Shadow host: The regular DOM node that the shadow DOM is attached to.
- Shadow root: The root node of the shadow tree.
- Shadow tree: The DOM tree inside the shadow DOM.
- Shadow boundary: the place where the shadow DOM ends, and the regular DOM begins.
The difference is that none of the code inside a shadow DOM can affect anything outside it, allowing for handy encapsulation.
### Create Shadow DOM
```javascript
let shadowRoot = elementRef.attachShadow({mode: 'open'});
var para = document.createElement('p');
shadowRoot.appendChild(para);
```
### Styling Shadow DOM with style element.
```javascript
// Create some CSS to apply to the shadow dom
var style = document.createElement('style');
style.textContent = `
.wrapper {
position: relative;
}
.info {
font-size: 0.8rem;
width: 200px;
display: inline-block;
border: 1px solid black;
padding: 10px;
background: white;
border-radius: 10px;
opacity: 0;
transition: 0.6s all;
position: absolute;
bottom: 20px;
left: 10px;
z-index: 3;
}
img {
width: 1.2rem;
}
.icon:hover + .info, .icon:focus + .info {
opacity: 1;
}`;
```
### customElements.define
```javascript
<script>
// Define the new element
customElements.define('popup-info', PopUpInfo);
</script>
<popup-info img="img/alt.png" text="Your card validation code (CVC) is an extra
security feature — it is the last 3 or 4
numbers on the back of your card.">
```
```javascript
customElements.define('word-count', WordCount, { extends: 'p' });
```
word-count : Tag/Element name, extends 'p' tag/element
WordCount : class (js class, not css class)
[Full Example](https://github.com/mdn/web-components-examples/blob/master/life-cycle-callbacks/main.js)
https://lit-element.polymer-project.org/
Using Template and slot
*.js*
```javascript
customElements.define('my-paragraph',
class extends HTMLElement {
constructor() {
super();
let template = document.getElementById('my-paragraph');
let templateContent = template.content;
const shadowRoot = this.attachShadow({mode: 'open'})
.appendChild(templateContent.cloneNode(true));
}
}
);
```
*.html*
```html
<template id="my-paragraph">
<style>
p {
color: white;
background-color: #666;
padding: 5px;
}
</style>
<p><slot name="my-text">My default text</slot></p>
</template>
<my-paragraph>
<span slot="my-text">Let's have some different text!</span>
</my-paragraph>
```
## Virtual DOM
React (Undo/Redo/History/Preload)
WebComponent
props & state
## Content Script - Work in Isolated Worlds
For Extension, no shared DOM.
Communicate via chrome.runtime (event/message)
Tab/Badge consideration
## Rendering
Node Tree
Render Tree (for JS/CSS/SVG Animation)
Update Dirty Element/Node/Rect only.
Graphics Context (canvas/webgl)
# Network
## XHTTPRequest
ajax (過時了?)
## Fetch
**POST Request**
```javascript
postData('http://example.com/answer', {answer: 42})
.then(data => console.log(data)) // JSON from `response.json()` call
.catch(error => console.error(error))
function postData(url, data) {
// Default options are marked with *
return fetch(url, {
body: JSON.stringify(data), // must match 'Content-Type' header
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, *omit
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
},
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
})
.then(response => response.json()) // 輸出成 json
}
```
**Upload a file**
```javascript
var formData = new FormData();
var fileField = document.querySelector("input[type='file']");
formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);
fetch('https://example.com/profile/avatar', {
method: 'PUT',
body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
```
https://github.com/mdn/fetch-examples/
## WebSocket (Socket.io)
###### tags: `javascript` `ecmascript` `ecma-262`