Skip to content

Javascript

从 HTML5 学习笔记独立出,沿用习惯用语 JS 是 ES6 的一种实现。

Learning material#

Syntax#

resouces:#

  • https://es6.ruanyifeng.com/
  • Promise (also for async)

book#

https://bonsaiden.github.io/JavaScript-Garden/ https://johnresig.com/apps/learn/ https://eloquentjavascript.net/

Syntax Learning Note#

Promise#

AKA: passing return outside callback

1
2
3
4
async function af() {
  const response = await somefuncReturnsPROMISE();
  // 'await' here resolves the promise.
}

E.g. in fetch :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// the two versions do the same thing.
// Promise.then version
await fetch("http://URL/", { SOME: PARAM }).then(async (response) => {
    await response.json().then((data) => {
        out = data;
    });
});

// async version :
let response = await fetch("http://URL/", { SOME: PARAM });
return await response.json();

https://es6.ruanyifeng.com/#docs/async#%E8%AF%AD%E6%B3%95 Master ruanyifeng tells us use more async than Promise. (async is prefered)

source

Promise in Debug

  • Catch statement does not catch thrown error: https://stackoverflow.com/questions/16316815/catch-statement-does-not-catch-thrown-error

    The reason why your try catch block is failing is because an ajax request is asynchronous. The try catch block will execute before the Ajax call and send the request itself, but the error is thrown when the result is returned, AT A LATER POINT IN TIME. When the try catch block is executed, there is no error. When the error is thrown, there is no try catch. If you need try catch for ajax requests, always put ajax try catch blocks inside the success callback, NEVER outside of it.

Array#
  • for an accumulative iteration, use Array.reduce() instead of Array.forEach()
Blob#

blob types:

1
2
 "text/plain;charset=utf-8",
 'application/json'
Typed Array#
  • like Uint8Array Uint16Array
  • Uint8Array.name string value of the constructor name Uint8Array
  • Uint8Array.prototype.buffer convert to arraybuffer
ArrayBuffer & TypedArray#

const buffer = new ArrayBuffer(8); const uint16 = new Uint16Array(buffer); This is like new Array(onearg), which defines the length.

encoding an TypedArray#
  • From topic: encoding a blob. first tried FileReader: https://developer.mozilla.org/en-US/docs/Web/API/FileReader. from https://medium.com/programmers-developers/convert-blob-to-string-in-javascript-944c15ad7d52
  • TextDecoder and TextDecoderfor encoding an Uint8Array wont work if the Array has an element like 255,243,88 etc
  • String: works either: JS encoded = String.fromCharCode.apply(null, u8) decoded = encoded.split('').map(c=>c.charCodeAt(0))

    but, with U16, it works. from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String (search array buffer to string instead of U8intArray)

Uint8Array & Array :

1
2
SOMEARR ==[`${new Uint8Array([SOMEARR])`] //(mod 256)
// ALL ELEMENTS OF SOMEARR < 256
callback function:#

JS 中可以 return 一个 function,(调用该 function 而不是 return 这个 function 的 return 值)

scrollTop#

Scrolltop Property 2019-11-14 04:22:47 scrollIntoView 作用类似微信中的 scroll-into view element.scrollIntoView();会移动整个页面,想只移动 div 中的内容要用 element. scrollTop scrollTop 是元素的属性,控制(滚动)的是外面的 scrollable div。

https://stackoverflow.com/a/1592609/10942184 You need to get the top offset of the element you'd like to scroll into view, relative to its parent (the scrolling div container): var myElement = document.getElementById('element_within_div'); var topPos = myElement.offsetTop; The variable topPos is now set to the distance between the top of the scrolling div and the element you wish to have visible (in pixels). Now we tell the div to scroll to that position using scrollTop: document.getElementById('scrolling_div').scrollTop = topPos; If you're using the prototype JS framework, you'd do the same thing like this: var posArray = $('element_within_div').positionedOffset(); $('scrolling_div').scrollTop = posArray[1]; Again, this will scroll the div so that the element you wish to see is exactly at the top (or if that's not possible, scrolled as far down as it can so it's visible).

但不是下面这样用: 文中的 divElem 不是想看到的 Elem,而是包含它的 div You would have to find the position of the element in the DIV you want to scroll to, and set the scrollTop property. divElem.scrollTop = 0; Update: Sample code to move up or down function move_up() { document.getElementById('divElem').scrollTop += 10; }

function move_down() { document.getElementById('divElem').scrollTop -= 10; } source

select HTML element#

Queryselector And Queryselectorall Vs Getelementsbyclassname And Getelementbyid

2019-11-14 04:22:44 简单说 getElementsByClassName 根据页面变化实时更新 querySelectorAll 不根据页面变化更新(因此应该占用资源更少?)

#对应 id .对应 class Also [] is a selector

About the differences, there is an important one in the results between querySelectorAll and getElementsByClassName: the return value is different. querySelectorAll will return a static collection, while getElementsByClassName returns a live collection. This could lead to confusion if you store the results in a variable for later use:

A variable generated with querySelectorAll will contain the elements that fulfilled the selector at the moment the method was called. A variable generated with getElementsByClassName will contain the elements that fulfilled the selector when it is used (that may be different from the moment the method was called). For example, notice how even if you haven't reassigned the variables aux1 and aux2, they contain different values after updating the classes:

// storing all the elements with class "blue" using the two methods var aux1 = document.querySelectorAll(".blue"); var aux2 = document.getElementsByClassName("blue");

// write the number of elements in each array (values match) console.log("Number of elements with querySelectorAll = " + aux1.length); console.log("Number of elements with getElementsByClassName = " + aux2.length);

// change one element's class to "blue" document.getElementById("div1").className = "blue";

// write the number of elements in each array (values differ) console.log("Number of elements with querySelectorAll = " + aux1.length); console.log("Number of elements with getElementsByClassName = " + aux2.length); .red { color:red; } .green { color:green; } .blue { color:blue; }

1
2
3
<div id="div0" class="blue">Blue</div>
<div id="div1" class="red">Red</div>
<div id="div2" class="green">Green</div>

来源:https://stackoverflow.com/a/39213298

e.g. document.querySelector("#demo")

Operators 运算符#
  • ^: XOR 异或
1
2
true ^ true //0
true ^ false //1
  • < a<x<b 的运算顺序是 a<x ? true : false 然后再将 true/false 和 b 比较大小。
Events#
Target & Currenttarget#

They are different. 2019-11-12 21:11:57 按钮点击的时间发生时候,传递的参数不同,容易搞混。

Arrow Function#
  • In case of returning an object, to distinguish from code block, use bracket ()

    argin => ({foo: bar})

  • 不绑定 this
  • 不绑定 arguments
  • 箭头函数没有 prototype 属性。
  • 没有 yield 关键字。
  • Should be prefered than normal function
Function Arguments#

func(a=1,b) is same as func(a=1,b=undefined) 输入一个数据时 a 的值会被覆盖,b 未赋值。可省略参数只能写在后面。(实际上都可以省略,调用的时候显示未赋值而已。)

剩余参数(...restArgs)

  • restArgs 包含所有实参 restArgs 是真数组(因此有.length 方法) restArgs 没有附加属性(如 callee)
Variable types#
  • ==的强制转换:
1
2
3
4
5
6
{}=={} // 返回 false
{}==true // 返回 false
{}==false // 返回 false
[]==[] // 返回 false
[]==true // 返回 false
[]==false // 返回 true // !!
  • force conversion of If If 后加任何非空变量都会执行。因此如果想判断 array 是否为空,最好直接 if (array.length) 来源:https://stackoverflow.com/questions/24403732/check-if-array-is-empty-or-does-not-exist-js If([])会执行 If(![])不会执行 If({})会执行,判断是否为空可以 if(Object.keys({})),不会执行 If(!{})不会执行 If([]==true)不会执行。 参考:https://blog.csdn.net/bnk_along/article/details/89521582

code review#

oneline code#

http://www.p01.org/256b_mars_canvas/ http://ourjs.com/detail/54be0a98232227083e000012 https://gist.github.com/addyosmani/fd3999ea7fce242756b1 (CSS Layout Debugger)

1
2
3
4
5
6
7
8
[...new Set(arr)] //- 数组去重 ES6
window.onerror = e => window.location = `http://stackoverflow.com/search?q=${e.message}`;  //(auto-search on stackoverflow)
javascript:alert("密码:"+document.querySelectorAll("input[type=password]")[0].value); //(show password)
$$('img').forEach(item => Object.assign(document.createElement('a'),{href:item.src,download:"download"}).click()); //(download all img)
$$('img').forEach(item => Object.assign(document.createElement('a'),{href:item.dataset.original || item.dataset.actualsrc || item.src,download:"download"}).click()) //(download all img for real-time rendered pages like Zhihu)
document.body.contentEditable='true'; document.designMode='on'; void 0  //(edit webpage)
[...document.getElementsByTagName('*')].forEach(x => x.oncopy = function(){})  //(override oncopy function)
document.body.style.cursor="none";  //(mouse style)

https://github.com/rapidhere/fpjs //(convert JS to one line) https://github.com/csvoss/onelinerizer //(convert python to one line) http://www.jsfuck.com/ //(convert JS to chars)

eg: (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]] explain: JS variable type conversion https://www.cnblogs.com/lvdabao/p/4280518.html

API#

Genaral#

response.header#

Header contains a hidden (invisible in console) JSON https://developer.mozilla.org/zh-CN/docs/Web/API/Headers To check the entries, use

1
2
3
for (var pair of myHeaders.entries()) {
    console.log(pair[0] + ": " + pair[1]);
}

https://developer.mozilla.org/zh-CN/docs/Web/API/Headers/entries

response.body#

when fetching some API, like WordsAPI + fetch. should write an async function to help the function works correctly. after the fetch, apply .json() to the response

  • https://stackoverflow.com/questions/45366789/trying-to-access-response-data-using-fetch
  • https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
  • https://developer.mozilla.org/en-US/docs/Web/API/Body/json

Web API#

setTimeout#

可以给 setTimeout 加一个终止:clearTimeout

1
2
timeoutID = window.setTimeout(window.alert, 2\*1000, 'That was really slow!');
clearTimeout(timeoutID);

source 2019-11-20 08:13:23

Date Object#
1
2
3
4
5
6
7
8
var date = new Date();
date.setHours(25);
date.setMinutes(61);
date.setSeconds(61);
// 超过的时间会自动进位,所以可以直接使用 Date 对象进行加减运算
date.setHours(date.getHours() + 1); // 当前时间加 1 小时
date.setHours(date.getHours() - 1); // 当前时间减 1 小时
date.setHours(date.getHours() + 1.9); // 小数会自动取整,即只增加 1 个小时

webpack file import (and export)#

import: https://webpack.js.org/api/module-methods/ there are several import -s in JS.. require : old staff

  • webpack can resolve json, js file with native support.
1
2
3
4
var SaveFile = require("file-saver").saveAs;
// the two below are the same.
import Polly from "@/assets/js/Polly.js";
import(NON_VAR_ONLY_STRING_CAN_USE_BACK_TICKS)
  • function(string path):Promise
  • can be called dynamically on runtime.
  • Magic Comments

P.S. export: I use FileSaver.js from GitHub

Web Crypto API#

Key -> plain text : exportKey plain text -> Key : importKey

Key -> encrypted text : wrapKey encrypted text -> Key : unwrapKey

generateKey & exportKey#

Promise Ver.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function generatePlainKey() {
    return window.crypto.subtle
        .generateKey({ name: "AES-CTR", length: 256 }, true, ["encrypt", "decrypt"])
        .then((ki) => {
            return window.crypto.subtle.exportKey("raw", ki);
        })
        .then((p) => {
            return `[${new Uint8Array(p)}]`;
        });
}
generatePlainKey().then((pr) => {
    console.log(pr);
});

async Ver.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
async function generateKey(rawKey) {
    let ki = await window.crypto.subtle.generateKey(
        { name: "AES-CTR", length: 256 },
        true,
        ["encrypt", "decrypt"]
    ); // ki: generated Key class object

    let planeArr = `[${new Uint8Array(
        await window.crypto.subtle.exportKey("raw", ki)
    )}]`;
    // promise with return r.
    let generatedKey = await window.crypto.subtle.importKey(
        "raw",
        rawKey,
        "AES-CTR",
        true,
        ["encrypt", "decrypt"]
    ); // same as ki
}

Code style#

  • focus on the logic / readability, not the performance. Idea from:

    Array.push and Array.length appending performance: They are different across the browsers. This is the answer persuaded me to not dwell on this: https://coderwall.com/p/kvzbpa/don-t-use-array-foreach-use-for-instead

  • Label statement can be used for specify a loop for continue and break
  • The simplest loop in JS i found is for/while. For the clarity, i choose for.
  • 函数命名 https://www.cnblogs.com/dolphin0520/p/10567879.html
  • below from https://juejin.im/post/6844903840030932999
  • === is faster than ==
  • Array.isArray(obj); // its a new Array method instead of arr instanceof Array;
  • myArray.length = 0; myArray will be equal to []. myArray.length = 4; first 4 elements.
  • delete items[3]; is same as item[3] = undefined. should items.splice(3,1);
  • for(var i = 0, len = arrayNumbers.length; i < len; i++) {}. len只会计算一次。

habit#

  • ALWAYS use [] Array constructor. https://coderwall.com/p/h4xm0w/why-never-use-new-array-in-javascript (one exception: to allocate memory)

    Array.forEach is about 95% slower than for() in for each for Arrays in JavaScript.So, don't use: I completely disagree with this & please allow to explain why: Using for loops is easy enough sure, in most cases.. but your overlooking the entire point of map, forEach, filter, reduce, find, findIndex, etc... These make use of functional paradigms, and these paradigms exist for a reason. Functional programming is all about focusing not on how to solve problems and instead, shifts your focus to what to solve. Beyond that, anyone who is worried about performance at this level can go back after they've built their app and measure the app's performance and decide where to optimize (and I will bet you will find most of your optimizations will not be refactoring map, filter, or reduce). Using for loops is like going backwards, not to mention that forEach is slow because it is modifying/mutating the original array, whereas .map() returns a new array, is much faster, and without the side effect of mutating the original array. There are fewer and fewer cases where a for loop is viable. Currently, the best use case would be for something like iterating an async generator function using the new for-await-of syntax. Which is super cool. My point is that javascript gives us first class functions and in combination with it's (sorta) functional paradigms can produce: more readable code, better tooling, composition and patterns like higher order functions and currying, beautiful immutability, and many other wins such as referential transparency with pure functions - which reduce side effects and can increase run time, especially if memorized etc... Although I say "sorta" as obviously you're going to need functions with side effects to make API calls, or logs, or write i/o (any function that has I/O). Regardless, I hate to say that I think you're giving bad advice here. Sure, everyone should know how to use all the original loops in javascript and use them well, but using a for loop versus map or even forEach (if you just have to mutate that existing array) is going the wrong direction.

    In short, My object is not to enhance the performance of these browsers. As long as it doesn't bother me, I will use the better, modern, comprehensible .push()and forEach() instead of those might have better performance a[a.length] and for.

  • ev.target.nodeName == "INPUT" && ev.target.type == "text" decide if is focusing on input box

Debug#

Broswer Error handling#

DOMException object: search tag: err handle

MDN DOMException says it's legacy, but I find this useful (with a table): dottoro

Functionalities#

(some should be added to user-snippet) like var x = Math.floor(Math.random() * (max - min + 1)) + min; [...Array(20).keys()]; // genearte 0,...,19

Pure CSS/JS Toast Notification#

Redirect a Webpage#

Web manipulation#

1
2
3
4
5
// Simulate a mouse click:
window.location.href = "http://www.w3schools.com"; // also for get url of current page.

// Simulate an HTTP redirect:
window.location.replace("http://www.w3schools.com");

HTML elements manipulation:#

new element#
1
2
3
4
// h1
var h1 = document.createElement("H1");
var textnode = document.createTextNode("This webpage is downgraded by Pablion");
h1.appendChild(textnode);
add element#
  • to firstChild of page
1
2
var pagebody = document.getElementsByTagName("BODY")[0];
pagebody.insertBefore(elem,pagebody.firstChild); // add to first place
set attribute#
1
elem.setAttribute("ATTR_KEY","ATTR_VALUE");
select element#
  • document.querySelector select only one element. (first match) 只选择第一个元素 support multiple querys 可以匹配多个标签 eg.

    document.querySelector("h2, h3").style.backgroundColor = "red";

    <h3>A h3 element</h3> > <h2>A h2 element</h2> will only affect the h3 tag

  • document.querySelectorAll() apply forEach on the returned array is common.
  • document.getElementsByTagName()
HTML iframe element#

cannot access(read) an element directly from iframe. neither with a new var.

解构赋值#

可以用来:

  • 忽略某些返回值
  • 交换变量
1
2
3
4
5
[a, b] = [10, 20]
[a, b, ...rest] = [10, 20, 30, 40, 50]
{ a, b } = { a: 10, b: 20 })
({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40})
console.log(rest); //{c: 30, d: 40}

Tools#

test frameworks#

Mocha is a JavaScript test framework! Chai is a BDD / TDD assertion library