1. javascript運(yùn)行環(huán)境 Node.js
Node.js or Node is an open-source, cross-platform, JavaScript runtime environment(JSRE) that executes JavaScript code outside of a web browser. Node.js基于 Chrome V8 引擎
Node.js只是javascript runtime之一,瀏覽器也是javascript runtime
Q&A
- javascript是單線程的,為什么可以執(zhí)行異步操作?
如以下異步代碼,console.log("Asynchronous")需要10秒后才執(zhí)行,但它不會(huì)blockconsole.log(greet_two);
setTimeoutis asynchronous so it runs in background, allowing code after it to execute while it runs
let greet_one = "Hello"
let greet_two = "World!!!"
console.log(greet_one)
setTimeout(function(){
console.log("Asynchronous");
}, 10000)
console.log(greet_two);
因?yàn)閞untime維護(hù)了線程池,所有的異步操作都交給了runtime執(zhí)行
之所以說JavaScript是單線程,是因?yàn)闉g覽器在運(yùn)行時(shí)只開啟了一個(gè)JS引擎線程來解析和執(zhí)行JS。為什么只有一個(gè)引擎呢?如果同時(shí)有兩個(gè)線程去操作DOM,涉及線程安全問題,導(dǎo)致渲染頁面的復(fù)雜度增加
雖然JavaScript是單線程的,可是瀏覽器內(nèi)部不是單線程的。一些I/O操作、定時(shí)器的計(jì)時(shí)和事件監(jiān)聽(click, keydown...)等都是由瀏覽器提供的其他線程來完成的
2. 運(yùn)行環(huán)境管理 using nvm
像go/python一樣,Node.js也有很多版本,我們可以通過nvm選擇特定版本的Node.js作為運(yùn)行環(huán)境
nvm: Node Version Manager.
install nvm: https://github.com/nvm-sh/nvm
nvm usage:
nvm install 16.19.1
$ nvm use 16
Now using node v16.9.1 (npm v7.21.1)
$ node -v
v16.9.1
3. javascript包管理 using NPM
NPM is a package manager for Node.js packages, which will allow you to install third party libraries (other people's code) by using the command line.
www.npmjs.com hosts thousands of free packages to download and use. The NPM program is installed on your computer when you install Node.js.
usage:
npm install <foo> # add the <foo> dependency to your project
4. 語法
變量/常量定義
變量定義使用let
常量定義使用const
> let b = 1
undefined
> b = 2
2
>
> const a = 1
undefined
> a = 2
Uncaught TypeError: Assignment to constant variable.
>
> const obj = {a:1}
undefined
> obj.a = 2 // 正常賦值。因?yàn)閛bj是一個(gè)引用,記錄的是內(nèi)存地址,引用本身并沒有發(fā)生變化
2
>
如果你需要一個(gè)不變的值,并且確定它的值在聲明后不會(huì)更改,那么使用 const 可以幫助減少代碼中錯(cuò)誤賦值的可能性。
如果需要可變的量,那么使用 let 即可
注意:對(duì)于引用類型的變量,==和===只會(huì)判斷引用的地址是否相同,而不會(huì)判斷對(duì)象具體里屬性以及值是否相同
支持embedding方式創(chuàng)建object
js支持通過embedding創(chuàng)建object,類似golang中使用struct-embedding創(chuàng)建新struct一樣(https://gobyexample.com/struct-embedding)
如果你不需要給object的屬性再起名字,可以直接通過embedding創(chuàng)建object
> let name = "john"
undefined
> let age = 10
undefined
> let doHomeWork = () => { return "doing homework" }
undefined
> let student1 = {name, age, doHomeWork} // create obj by embedding other objs
undefined
> student1
{ name: 'john', age: 10, doHomeWork: [Function: doHomeWork] }
>
js is Prototype-based
js is a Prototype-based language. Prototype-based programming is a style of object-oriented programming in which classes are not explicitly defined, but rather derived by adding properties and methods to an instance of another class or, less frequently, adding them to an empty object.
In simple words: this type of style allows the creation of an object without first defining its class.
// Example of true prototypal inheritance style
// in JavaScript.
// object creation using the literal
// object notation {}.
const foo = { name: "foo", one: 1, two: 2 };
// Another object.
const bar = { two: "two", three: 3 };
// Object.setPrototypeOf() is a method introduced in ECMAScript 2015.
// For the sake of simplicity, let us pretend
// that the following line works regardless of the
// engine used:
Object.setPrototypeOf(bar, foo); // foo is now the prototype of bar.
// If we try to access foo's properties from bar
// from now on, we'll succeed.
bar.one; // Resolves to 1.
// The child object's properties are also accessible.
bar.three; // Resolves to 3.
// Own properties shadow prototype properties
bar.two; // Resolves to "two"
bar.name; // unaffected, resolves to "foo"
foo.name; // Resolves to "foo"
For another example:
const foo = { one: 1, two: 2 };
// bar.[[prototype]] = foo
const bar = Object.create(foo);
bar.three = 3;
bar; // { three: 3 }
bar.one; // 1
bar.two; // 2
bar.three; // 3
原型
JavaScript 中所有的對(duì)象都有一個(gè)內(nèi)置屬性,稱為它的 prototype(原型)。它本身是一個(gè)對(duì)象,故原型對(duì)象也會(huì)有它自己的原型,逐漸構(gòu)成了原型鏈。原型鏈終止于擁有 null 作為其原型的對(duì)象上。
有個(gè)對(duì)象叫 Object.prototype,它是最基礎(chǔ)的原型,所有對(duì)象默認(rèn)都擁有它。Object.prototype 的原型是 null,所以它位于原型鏈的終點(diǎn)
設(shè)置原型
使用 Object.create
const personPrototype = {
greet() {
console.log("hello!");
},
};
const carl = Object.create(personPrototype); // create改叫createFrom更貼切
carl.greet(); // hello!
使用構(gòu)造函數(shù)
如果我們?cè)O(shè)置一個(gè)構(gòu)造函數(shù)的 prototype,我們可以確保所有用該構(gòu)造函數(shù)創(chuàng)建的對(duì)象都被賦予該原型:
const personPrototype = {
greet() {
console.log(`你好,我的名字是 ${this.name}!`); // `this`就是調(diào)用方
},
};
// 構(gòu)造函數(shù)Person
function Person(name) {
this.name = name;
this.sing = function() {
console.log(`${this.name} singing!`); // `this`就是調(diào)用方
}
}
Object.assign(Person.prototype, personPrototype);
const carl = new Person('carl');
carl.greet(); // 你好,我的名字是 carl!
// new 在執(zhí)行時(shí)會(huì)做四件事情:
// ??① 在內(nèi)存中創(chuàng)建一個(gè)新的空對(duì)象。
// ??② 讓 this 指向這個(gè)新的對(duì)象。
// ??③ 執(zhí)行構(gòu)造函數(shù)里面的代碼,給這個(gè)新對(duì)象添加屬性和方法。
// ??④ 返回這個(gè)新對(duì)象(所以構(gòu)造函數(shù)里面不需要 return )
const bob = new Person('bob');
console.log(bob.greet === carl.greet); // True. 因?yàn)槎际且胮rototype.greet -> 構(gòu)造函數(shù)通過原型分配的函數(shù)是所有對(duì)象所共享的
console.log(bob.sing === carl.sing); // false. 自有的sing各自持有而不是引用同一份method,其實(shí)挺浪費(fèi)內(nèi)存空間的
Named Export and Default Export
Default Export (export default)
You can have one default export per file. When you import you have to specify a name and import like so:
import MyDefaultExport from "./MyFileWithADefaultExport";
You can give this any name you like.
Named Export (export)
With named exports, you can have multiple named exports per file. Then import the specific exports you want surrounded in braces:
// ex. importing multiple exports:
import { MyClass, MyOtherClass } from "./MyClass";
// ex. giving a named import a different name by using "as":
import { MyClass2 as MyClass2Alias } from "./MyClass2";
// use MyClass, MyOtherClass, and MyClass2Alias here
Or it's possible to use a default along with named imports in the same statement:
import MyDefaultExport, { MyClass, MyOtherClass} from "./MyClass";
js語言特性
數(shù)據(jù)類型體系
-
基本數(shù)據(jù)類型。基本數(shù)據(jù)類型對(duì)象直接分配在棧中,如數(shù)字、字符串,然后持有引用
JavaScript 中的基本數(shù)據(jù)類型(例如字符串、數(shù)字、布爾值等)是不可變的。這意味著當(dāng)你重新為一個(gè)變量賦值時(shí),實(shí)際上是在內(nèi)存中創(chuàng)建了一個(gè)新的值,并將變量引用這個(gè)新值。原始值在內(nèi)存中的位置不會(huì)改變,但變量引用的內(nèi)存地址將指向新值。
let s = 'hello' s = 'world'在這個(gè)例子中,我們首先將變量 s 賦值為字符串 'hello'。當(dāng)我們將 s 重新賦值為 'world' 時(shí),實(shí)際上是創(chuàng)建了一個(gè)新的字符串 'world',并將變量 s 指向這個(gè)新字符串。原始字符串 'hello' 仍然存在于內(nèi)存中,但變量 s 現(xiàn)在引用的是新字符串 'world' 的內(nèi)存地址。
容器類型。容器類型的對(duì)象分配在堆中,然后在棧中持有堆對(duì)象的引用
動(dòng)態(tài)語言,變量xxx的類型可以隨便改,如xxx = 1然后xxx = [1,2,3],這個(gè)特性決定了動(dòng)態(tài)語言都要依賴引用(實(shí)際上是對(duì)指針的再封裝)。如果像golang把xxx固定為一個(gè)int長度的內(nèi)存空間,那xxx就無法再被賦值為數(shù)組了。對(duì)任何數(shù)據(jù)類型重新賦值,都是修改了引用;而對(duì)引用的容器類型對(duì)象進(jìn)行局部修改,不改變引用本身,整體和Python的類型機(jī)制相同??梢妱?dòng)態(tài)語言出于其動(dòng)態(tài)特性,類型系統(tǒng)都是一個(gè)套路
因此,js的const,it does not define a constant value. It defines a constant reference to a value instance.
值傳遞
在 JavaScript 中,所有參數(shù)都是按值傳遞的。但是對(duì)于對(duì)象(包括數(shù)組和函數(shù)),傳遞的值本身是對(duì)象的引用,引用作為值,因此它們的行為看起來像是按引用傳遞。這樣的話,如果你在函數(shù)內(nèi)部修改了一個(gè)對(duì)象參數(shù)的屬性,那么在函數(shù)外部,這個(gè)屬性的原始對(duì)象也會(huì)被修改。
JavaScript 有 5 種基本的數(shù)據(jù)類型,分別是:bool、null、undefined、string 和 number,還有另外三種引用類型: Array、Function 和 Object。此外,在 ES6 中,引入了一種新的數(shù)據(jù)類型 - Symbol - 是一種原始數(shù)據(jù)類型,像數(shù)字或字符串一樣按值進(jìn)行傳遞。
總結(jié)一下,基本類型(Number、String、Boolean、Undefined、Null、Symbol)由于空間小、數(shù)據(jù)簡單,Javascript按值傳遞(復(fù)制一份到新的棧),引用類型(Object)由于空間大、數(shù)據(jù)復(fù)雜,Javascript按共享傳遞(復(fù)制引用到新的棧)。