背景
所有内容大致基于freecodecamp,是一些总结出来常用的需要记住的内容。
basic JavaScript
这个部分是113个小题目,我觉得难点是 Recursion 以及针对对象的各种处理。
JavaScript object
object值的覆盖
const cat = {
"name": "Whiskers",
"legs": 4,
"tails": 1,
"enemies": ["Water", "Dogs"]
};
变为如下以后,legs的值变为了5,也没报错。
const cat = {
"name": "Whiskers",
"legs": 4,
"legs": 5,
"tails": 1,
"enemies": ["Water", "Dogs"]
};
对象的properies命名有几种方式
如果prop有空格,必然需要引号来括号起来:
const cat = {
"the name": "Whiskers",
"how much legs": 4,
"howMuch tails": 1,
"whois enemies": ["Water", "Dogs"]
};
如果prop没有空格,那么就爱用不用引号:
const anotherCat = {
name: "niconico",
legs: 4,
tails: 1,
enemies: ["Water", "Dogs"]
};
获取对象属性 access object prop
如果prop没空格,可以采用notation(.
)来获取
const anotherCat = {
name: "niconico",
legs: 2,
tails: 9,
enemies: ["vegetables", "DRM"]
};
console.log(anotherCat.name)
// niconiconi
如果prop有空格,只能采用bracket notation([]
)来获取
const cat = {
"the name": "Whiskers",
"how much legs": 4,
"howMuch tails": 1,
"whois enemies": ["Water", "Dogs"]
};
console.log(cat["the name"])
// Whiskers
Nested Object情况,需要保证以上两点。
删除对象的prop, delete objects prop
只需要在获取的添加一个关键词 delete
const cat = {
"name": "Whiskers",
"legs": 4,
"tails": 1,
"enemies": ["Water", "Dogs"]
};
/**step 1
删除掉的是该元素内容,而非元素**/
delete cat.enemies[0]
console.log(cat.enemies)
// [null,"Dogs"]
/**step 2
删除掉的是元素**/
delete cat.legs
console.log(cat)
/**
{name: 'Whiskers', tails: 1, enemies: Array(2)}
**/
对象是否有某个prop
method of objects .hasOwnProperty(propname)
const niconiconi = {
name: "niconiconi",
hobbies: ["electric","computer tech"],
enemies: ["GOV", "cat"],
1:100
};
/**step 1 ,
双引号的存在还是很必要的,即便原本的prop并没有引号。**/
console.log(niconiconi.hasOwnProperty("name"));
// true
console.log(niconiconi.hasOwnProperty(name));
// false
/**step 2 ,
这个是自然没有的**/
console.log(niconiconi.hasOwnProperty("weight"));
// false
/**step 3,
数字又可以被直接读到**/
console.log(niconiconi.hasOwnProperty(1));
// true
console.log(niconiconi.hasOwnProperty("1"));
// true
/**step 4,
问题再次出现,和name区别很大**/
console.log(niconiconi.hasOwnProperty("hobbies"));
// true
console.log(niconiconi.hasOwnProperty(hobbies));
// Uncaught ReferenceError: hobbies is not defined
prop in object
,
const niconiconi = {
name: "niconiconi",
hobbies: ["electric","computer tech"],
enemies: ["GOV", "Job"],
"sex":"cute girl",
1:100
};
console.log("name" in niconiconi);
// true
console.log(name in niconiconi);
// false
console.log("1" in niconiconi);
// true
console.log(1 in niconiconi);
// true
console.log("hobbies" in niconiconi);
// true
console.log("sex" in niconiconi);
// true
console.log(sex in niconiconi);
// Uncaught ReferenceError: sex is not defined
Destructuring Assignment about object
点击跳转
- Extract Values from Objects
- Extract Values and Rename from Objects
- Assign Variables from Nested Objects
Object Literal Declarations
这个还是值得说一下,主要是当你想当然的以为=>
后是{ ... }
,它给你一个({...})
, 一种简写形式可以说是。
const getMousePosition = (x, y) => ({
x: x,
y: y
});
// 推荐使用下面这种,更简单。
const getMousePosition = (x, y) => ({ x, y });
console.log(getMousePosition(2,4));
// {x: 2, y: 4}
// 另一个例子,我感觉这个生成对象的方法还是蛮简单的,毕竟很多时候确实, 懒得写。
const createPerson = (name, age, gender) => {
// Only change code below this line
return ({name,age,gender});
// Only change code above this line
};
const test = createPerson("tom",16,"F");
// test = {name: 'tom', age: 16, gender: 'F'}
Recursion 递归
先空起,做题是做起了,但是感觉再稍微难一点的绝对又做不起了。想法不是太清晰。
var,let,const的区别
var的使用:global,function,redeclared
下面这个用在了函数内,因此只有函数内的引用是合法的,全局并未定义过,所以会error
function numArray(num){
var numArray = [];
for (var i = 0; i < num; i++) {
numArray.push(i);
}
console.log(i);
return numArray;
}
console.log(numArray(5));
// 5
// [0, 1, 2, 3, 4]
console.log(i);
// error: i is not defined
但是,当全局有过这个东西,那么就会在这个层级去读取,而且,var的变量是可以在同一层级覆盖声明的。
var i = 100;
function numArray(num){
var numArray = [];
for (var i = 0; i < num; i++) {
numArray.push(i);
}
console.log(i);
return numArray;
}
console.log(numArray(5));
// 5
// [0, 1, 2, 3, 4]
console.log(i);
// 100
var i = 99;
console.log(i);
// 99
let的使用:block,statement,expression
下面这个使用了let,首先let的declaration的区域是比较狭窄的,可以比function更细的粒度。同时不能在一个作用区域覆盖声明。
let i = 100;
function numArray(num){
var numArray = [];
for (let i = 0; i < num; i++) {
numArray.push(i);
}
console.log(i);
return numArray;
}
console.log(numArray(5));
// 100
// [0, 1, 2, 3, 4]
console.log(i);
// 100
let i = 99;
console.log(i);
// error: 'i' has already been declared
尽管不能redeclared,但是let 可以进行内容的改写。reassign the value
let i = 100;
i = 99;
console.log(i);
// 99
let test = ["hello",1,2];
test = [100,99,98];
test[4] = "hellotest";
console.log(test);
// [100, 99, 98, empty, 'hellotest']
const的使用:Read-Only,Mutate Array&Function
const和let非常像,作用域类似,也不能redeclared。
但多了一条:不能进行内容改写 。Read-Only,NOT reassign the value
const i = 100;
i = 99;
console.log(i);
// TypeError: Assignment to constant variable.
当作为一个固定的常量时候,通常使用全大写和下滑线_
符号的作为声明。
const NUMBER = 100;
const MAX_NUM = 999;
const MIN_NUM = 0;
const 用于 Mutate an Array Declared,
const testArray = ["hello","你好","Bonjour"];
testArray[0]="halo";
console.log(testArray);
// ['halo', '你好', 'Bonjour']
console.log(testArray);
// TypeError: Assignment to constant variable.
const 用于object:
const testObject = {
1:100,
2:200,
3:300
};
testObject[1] = 111;
console.log(testObject);
// {1: 111, 2: 200, 3: 300}
testObject = {
1:111,
2:222,
3:333
};
console.log(testObject);
// TypeError: Assignment to constant variable.
const 在function中的表现:
function numArray(num){
const numArray = [];
for (let i = 0; i < num; i++) {
numArray.push(i);
}
console.log(i);
// i is not defined
return numArray;
}
console.log(numArray(5));
// [0, 1, 2, 3, 4]
上中let i = 0
倘若更改为const i = 0
则会报错(TypeError: Assignment to constant variable.
),因此需要变动的量,使用let
而非const
.
小结
- var ,let , const 都可以作为变量声明用。
- const更为常用,实在需要变通的地方,用let。ES6中这两更为常见,抛弃var吧。
- 即便是const 定义常规变量也会出现“一不小心mutation value”的情况,因此:
function freezeObj() {
const MATH_CONSTANTS = {
PI: 3.14
};
Object.freeze(MATH_CONSTANTS);
try {
MATH_CONSTANTS.PI = 99;
} catch(ex) {
console.log(ex);
}
return MATH_CONSTANTS.PI;
}
const PI = freezeObj();
// TypeError: Cannot assign to read only property 'PI' of object '#<Object>'
Object.freeze(obj);
也是更严格的“prevent object mutation” 手段。
Arrow Function
arrow function是ES6特有的,=>
作为特征。JavaScript中很喜欢直接声明变量后直接写函数对应起来,因此这样的箭头指向也具有识别度。
const name = function()
Anonymous function
东西多就放{},东西少就直接跟。anonymous函数感觉主要就是不传参,想看看实际项目里怎么用到的。
/*step 1: string */
const MARRY = () => "Marry Christmas";
console.log(MARRY(123));
// Marry Christmas
/* step 2 object */
const nico = {
name:"niconiconi",
sex:"girl"
}
const WIFE = () => nico;
console.log(WIFE(['smart','lovely','keyboard genius','cat','1999\'s after']));
// {name: 'niconiconi', sex: 'girl'}
/* step 3 Oprator*/
const test1 = ()=> true & true;
console.log(test1());
// 1
const test2 = ()=> true & false;
console.log(test2());
// 0
const test3 = ()=> 199+201;
console.log(test3());
// 400
/* step 4 function */
const magic =() => {return new Date();}
console.log(magic);
//() => {return new Date();}
console.log(magic(1));
// Wed Jan 12 2022 22:51:48 GMT+0800 (China Standard Time)
Parameters Function
普通一点的传参,要多少,传多少
const multiplier = (item, multi) => item * multi;
console.log(multiplier(1111,1111));
// 1234321
const arrConcat = (arr1,arr2) => {
return arr1.concat(arr2);
}
console.log(arrConcat(["hello","baby"],['Do ','you ','like ','me']));
// ['hello', 'baby', 'Do ', 'you ', 'like ', 'me']
稍微体面点的传参,稍微设定一下
const arrConcat = (arr1,arr2="sweet heart") => {
return arr1.concat(arr2);
}
console.log(arrConcat(['Do ','you ','love ','me']));
// ['Do ', 'you ', 'love ', 'me', 'sweet heart']
更弹性的传参,“来多少,收多少”
const sum = (...args) => {
const reducer = (a,b) => a+b;
return args.reduce(reducer);
}
console.log(sum(1,2,3,4,5,6));
// 21
扩展阅读
The rest parameter eliminates the need to check the args array and allows us to apply map(), filter() and reduce() on the parameters array.
Declarative Functions with ES6 in a Object
不得不说,对象里面定义函数,还是蛮不错的,简单好懂,结构也清晰。
const person = {
name: "Taylor",
sayHello() {
return `Hello! My name is ${this.name}.`;
},
age:18,
afterYears(num){
return `after ${num} years,he is ${this.age+num} years old.`
}
};
console.log(person.sayHello())
//Hello! My name is Taylor.
console.log(person.afterYears(15));
// after 15 years,he is 33 years old.
spread operator
ES6引入的一个操作,典型操作是expand/speard arrays
sum of number array
const arr = [6, 89, 3, 45];
const maximus = Math.max(...arr);
// maximus = 89
其他求和方法:for, reduce(), lodash
copy array to new array
// shallow copy
const arr = [6, 89, 3, 45];
const arr2 = [...arr];
// arr2 = [6, 89, 3, 45];
其他clone方法:
Destructuring Assignment
Destructuring assignment is special syntax introduced in ES6, for neatly assigning values taken directly from an object.'
Extract Values from Objects
const HIGH_TEMPERATURES = {
yesterday: 75,
today: 77,
tomorrow: 80
};
const {today,tomorrow} = HIGH_TEMPERATURES;
// today = 77
// tomorrow = 80
Extract Values and Rename from Objects
const HIGH_TEMPERATURES = {
yesterday: 75,
today: 77,
tomorrow: 80
};
const {today:hiToday,tomorrow:hiTomorrow} = HIGH_TEMPERATURES;
// hiToday = 77
// hiTomorrow = 80
Assign Variables from Nested Objects
const HIGH_TEMPERATURES = {
yesterday: 75,
today: {
low:66,
high:88,
avange:77
},
tomorrow: 80
};
const {today:{low:hiLT,high:hiHT}} = HIGH_TEMPERATURES;
// hiLT = 66;
// hiHT = 88;
Assign Variables from Arrays
var [a, b,,, c] = [1, 2, 3, 4, 5, 6];
// a = 1, b = 2;
// c = 5;
var [a,b,...c] = [1, 2, 3, 4, 5, 6];
// a = 1, b = 2;
// c = [3, 4, 5, 6];
Pass an Object as a Function’s Parameters
当需要传参的是一个对象,大概率不是要个对象,而是对象的一些 key 和 value,可以使用Destructuring Assignment的方式,省略函数内部取值的步骤。算是个简写方法,个人不太喜欢。
const stats = {
max: 56.78,
standard_deviation: 4.34,
median: 34.54,
mode: 23.87,
min: -0.75,
average: 35.85
};
// 一般写法
const half = (stats) => {
return (stats.min+stats.max)/2;
}
// Destructuring Assignment
const half = ({min,max}) =>{
return (min +max) /2;
}
Template Literals
a special type of string, multi-line, string interpolation.
string template literals
const result = {
success: ["max-length", "no-amd", "prefer-arrow-functions"],
failure: ["no-var", "var-on-top", "linebreak"],
skipped: ["no-extra-semi", "no-dup-keys"]
};
function makeList(arr) {
const failureItems = [];
for(let i = 0; i< arr.length; i++){
let item = `<li class=\"text-warning\">${arr[i]}</li>`
failureItems.push(item);
}
return failureItems;
}
const failuresList = makeList(result.failure);
// failuresList = [
// '<li class="text-warning">no-var</li>',
// '<li class="text-warning">var-on-top</li>',
// '<li class="text-warning">linebreak</li>'
// ]
class Syntax
和java啥的里面的class不是一个东西,只是一个比较特别的函数。
class Syntax to Define a Constructor Function
A class may only have one constructor, 一个class只能有一个constructor。我觉得也算对象快捷生成的一种办法。但这么看还是 Object Literal Declarations 这个方法更简洁。
class Vegetable {
constructor(name,weight,color){
this.name = name;
this.weight = weight;
this.color = color;
}
}
const carrot = new Vegetable('carrot',1.1,"light green");
console.log(carrot);
// {
// "name": "carrot",
// "weight": 1.1,
// "color": "light green"
// }
getters and setters to Control Access to an Object
getters 和setter乍看感觉没啥用,没啥优越性的感觉。
getter拿来取某个key的value,只是定义了一个简写,以前是“把狗王头上悬着的那把达摩克斯拿给我”,现在是“狗王之剑”/“kkj”(看看剑) ,这种“暗号”就知道啥意思。
setter 是 更改某个key的value,也是定义了一个简写,“今天起你就叫《大宝剑》了”,改成了“Hi,《大宝剑》”,没意思。
对同一个key来说,这两不用同时都必须写,按需要写。
class Vegetable {
constructor(name,weight,color){
this.name = name;
this.weight = weight;
this.color = color;
}
//getter
get wt(){
return this.weight
}
// setter
set wt(num){
this.weight = num;
}
}
const carrot = new Vegetable('carrot',1.1,"light green");
console.log(carrot);
// {name: 'carrot', weight: 1.1, color: 'light green'}
这种简写不是一个函数,虽然用了很像函数的结构。
console.log(carrot.wt);
// 1.1
console.log(carrot.wt());
// error: carrot.wt is not a function
可以看到实现了更改的效果,而且是深更改?shallow edit?有这个说法吗,类比shallow copy 和deep copy。
carrot.wt = 1.2
console.log(carrot.wt);
// 1.2
//carrot {name: 'carrot', weight: 1.2, color: 'light green'}
但是,不用这个方法,直接改也不是不行。所以不知道优势是什么。
carrot.name = "hello";
console.log(carrot);
// {name: 'hello', weight: 1.2, color: 'light green'}
Module Script
这个章节我觉得巨重要,一个项目结构和功能分布,让人读代码和用代码都爽得一批,怎么安排module是个学问大概。
个人觉得MDN的JavaScript modules 提供的modules example 是比较不错的,先下下来,不用每行都读懂,只看怎么安排import 和export和文件。
basic module
Simple example that demonstrates module basics
文件结构大概是这样,html的预览需要启用vscode里的live server,不然要报错,有个解决办法是file:// 这个就不学了。
- index.html
- src
- main.js
- lib
- counter.js
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试basic module的使用</title>
<!-- 引入了一个main.js -->
<script type="module" src="src/main.js"></script>
</head>
<body>
</body>
</html>
main.js
import { add,sub } from "../lib/counter.js";
const test1 = add(1,2);
const test2 = sub(5,100);
console.log(test1,test2);
// 这一行的输出,确实出现在index.html的console
counter.js
const add = (a ,b) => {
return a+b;
}
const sub = (a,b) => {
return b-a;
}
export {add,sub};
Export and Import Fallback with export default
export 的就是个函数的retun
,也只准file/module有一个。所以经常被用作fallback value for a file or module.
export default
不准用于 var ,let ,const
export default function subtract(x, y) {
return x - y;
}
在被引用的文件,就是如下导入
import subtract from "url"
subtract(7,4);
总结:import 和export是一对,按理说需要同时存在。html不是编程语言的原因:只是一个厨房,但是厨房能用起来主要还是靠里面的厨具和工具。
renaming
Shows how exports can be renamed to avoid conflicts, using x as y syntax
同上部分相比,变化如下:
main.js
import { add as newAdd } from "../lib/counter.js";
const test3 = newAdd(3,4);
console.log(test3);
感觉这个在引入多个js文件中的多个function时候有用,毕竟,干活用的英语单词就那么多,很容易重名。(侧面说明注释也很重要,不然看起来真的就跟“找不同”游戏有点像了。)
module-objects
Shows how to import an entire module as an object using import * as x from ‘y.js’ syntax
同上部分相比,变化如下: main.js
import * as counted from "../lib/counter.js";
const test4 = counted.add(5,6);
const test5 = counted.sub(1,10);
console.log(test4,test5);
这个办法蛮好的,更精简,也等于标注了内容的来源,看起来用起来肯定舒服的。
classes
importing a class from a module
这个要重新写class,重新写的内容比较多。 main.js
import {makeNum as test} from "../lib/counter.js";
const num1 = new test(100);
console.log(num1.value);
// 100
console.log(num1.cube());
// 10000
console.log(num1.cicleC());
// 628.3185307179587
counter.js
class makeNum{
constructor(value){
this.value = value;
}
cube() {
const cube = this.value * this.value;
return cube;
}
cicleC() {
return 2 * Math.PI * this.value;
}
}
export {makeNum};
module-aggregation
Shows how sub module features can be aggregated into a parent module using export { x } from ‘y.js’ syntax
就是当引入的js多了,有些可以合并,但是又不想真的合并成一个js文件,就会考虑用这个办法写个清单,最后的main.js
引用这个清单就好。
这个举例一下结构,假设原本的main.js
import {makeNum as make} from "../lib/file1.js";
import {sumNum as sum} from "../lib/file2.js";
import {subNum as sub} from "../lib/file3.js";
import {roundNum as round} from "../lib/file4.js";
import {bigNum as big} from "../lib/file5.js";
import {smallNum as small} from "../lib/file6.js";
确实太多了,而且又都是同类操作/同一个业务模块,就会新增一个文件夹在lib下,把文件挪进去。在number文件夹同级,新增一个文件:number.js
- lib
- numer
- file1.js
- file2.js
- file3.js
- file4.js
- file5.js
- file6.js
- number.js
- numer
number.js
export {makeNum as make} from "./number/file1.js";
export {sumNum as sum} from "./number/file2.js";
export {subNum as sub} from "./number/file3.js";
export {roundNum as round} from "./number/file4.js";
export {bigNum as big} from "./number/file5.js";
export {smallNum as small} from "./number/file6.js";
main.js 就会变成如下神清气爽的样子,但是看看number.js
,哪有什么岁月静好,都是别的文件为你负重前行……
import {make,sum,sub,round,big,small} from "../lib/number.js";
dynamic-module-imports
Demonstrates dynamic module loading using import().then()
稍微看了下,现在暂时没必要学,因为我写不出一个sample来说明。
JavaScript Promise
这个感觉是一种类似linux进程管理(阻塞,挂起,结果)
,或者代码中if... else...
, try...catch
这种对单元事件的流程管控。
估计是JavaScript中这种事情太多了,给整出一个这个来简化。
promise本身是个函数。
要么在pending,要么在resolve ,要么在reject,就这三个状态,对于一个事件。
悄咪咪:我确实对各种异常处理深恶痛绝。
Create a JavaScript Promise
new关键词配套的就是class函数,所以promise是函数没跑,下面例子中,Promise()
,()
中接受的是一个函数(resolve, reject) => {}
作为参数。
const makeServerRequest = new Promise((resolve, reject) => {
});
Complete a Promise with resolve and reject
就是两条路,resolve了就咋办,reject了就咋办。if else
的复杂版?
const makeServerRequest = new Promise((resolve, reject) => {
// responseFromServer represents a response from a server
let responseFromServer;
if(responseFromServer) {
resolve("We got the data");
} else {
reject("Data not received");
}
});
Handle a Fulfilled Promise with then
then等于是个额外操作,就是流程之上你再安排个流程。毕竟resolve和reject两个分支就会走一个,走完就直接关掉了。估计是这个原因,搞了个then用来延长整个函数的运行范围。
const makeServerRequest = new Promise((resolve, reject) => {
// responseFromServer is set to true to represent a successful response from a server
let responseFromServer = true;
if(responseFromServer) {
resolve("We got the data");
} else {
reject("Data not received");
}
});
// Add the then method to your promise. Use result as the parameter of its callback function and log result to the console.
makeServerRequest.then(result => {
console.log(result);
});
Handle a Rejected Promise with catch
感觉then不一定要有,但是catch一定要有….不然reject也不能说毫无意义吧就是写bug已经很多了,还要改,能轻松一点就轻松一点。
const makeServerRequest = new Promise((resolve, reject) => {
// responseFromServer is set to false to represent an unsuccessful response from a server
let responseFromServer = false;
if(responseFromServer) {
resolve("We got the data");
} else {
reject("Data not received");
}
});
makeServerRequest.then(result => {
console.log(result);
});
makeServerRequest.catch(error => {
console.log(error);
});
总结,可以看出来,promise主要还是个true为主,毕竟catch是reject运作才跑,没事抛出什么error?设计上应该是都是尽量走true,这样写起来也更墨守成规(褒义)。
完结
虽然FCC还有以下大章节
- Regular expressions
- Debugging
- Basic Data Structures
- Basic Algorithm Scripting
- Object Oriented Programming
- Functional Programming
- Intermediate Algorithm Scripting
- JavaScript Algorithms and Data Structures Projects
这么多大章节没搞,但是这篇博客我还是决定完结,因为REGEX完全值得单独开一篇记录…..
别的其实都是个大项目,再照抄FCC我感觉用处不大,不如之后单独再开。