背景
所有内容大致基于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我感觉用处不大,不如之后单独再开。