1. javascript運行環(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í)行,但它不會blockconsole.log(greet_two);
setTimeout
is 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);
因為runtime維護了線程池挥下,所有的異步操作都交給了runtime執(zhí)行
之所以說JavaScript是單線程,是因為瀏覽器在運行時只開啟了一個JS引擎線程來解析和執(zhí)行JS卢佣。為什么只有一個引擎呢欺缘?如果同時有兩個線程去操作DOM栋豫,涉及線程安全問題,導致渲染頁面的復雜度增加
雖然JavaScript是單線程的谚殊,可是瀏覽器內部不是單線程的丧鸯。一些I/O操作、定時器的計時和事件監(jiān)聽(click, keydown...)等都是由瀏覽器提供的其他線程來完成的
2. 運行環(huán)境管理 using nvm
像go/python一樣络凿,Node.js也有很多版本骡送,我們可以通過nvm
選擇特定版本的Node.js作為運行環(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 // 正常賦值。因為obj是一個引用絮记,記錄的是內存地址摔踱,引用本身并沒有發(fā)生變化
2
>
如果你需要一個不變的值,并且確定它的值在聲明后不會更改怨愤,那么使用 const
可以幫助減少代碼中錯誤賦值的可能性派敷。
如果需要可變的量,那么使用 let
即可
注意:對于引用類型的變量撰洗,==
和===
只會判斷引用的地址是否相同篮愉,而不會判斷對象具體里屬性以及值是否相同
支持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 中所有的對象都有一個內置屬性试躏,稱為它的 prototype(原型)。它本身是一個對象设褐,故原型對象也會有它自己的原型颠蕴,逐漸構成了原型鏈。原型鏈終止于擁有 null 作為其原型的對象上助析。
有個對象叫 Object.prototype犀被,它是最基礎的原型,所有對象默認都擁有它外冀。Object.prototype 的原型是 null寡键,所以它位于原型鏈的終點
設置原型
使用 Object.create
const personPrototype = {
greet() {
console.log("hello!");
},
};
const carl = Object.create(personPrototype); // create改叫createFrom更貼切
carl.greet(); // hello!
使用構造函數
如果我們設置一個構造函數的 prototype,我們可以確保所有用該構造函數創(chuàng)建的對象都被賦予該原型:
const personPrototype = {
greet() {
console.log(`你好雪隧,我的名字是 ${this.name}西轩!`); // `this`就是調用方
},
};
// 構造函數Person
function Person(name) {
this.name = name;
this.sing = function() {
console.log(`${this.name} singing!`); // `this`就是調用方
}
}
Object.assign(Person.prototype, personPrototype);
const carl = new Person('carl');
carl.greet(); // 你好,我的名字是 carl!
// new 在執(zhí)行時會做四件事情:
// ??① 在內存中創(chuàng)建一個新的空對象膀跌。
// ??② 讓 this 指向這個新的對象遭商。
// ??③ 執(zhí)行構造函數里面的代碼,給這個新對象添加屬性和方法捅伤。
// ??④ 返回這個新對象(所以構造函數里面不需要 return )
const bob = new Person('bob');
console.log(bob.greet === carl.greet); // True. 因為都是引用prototype.greet -> 構造函數通過原型分配的函數是所有對象所共享的
console.log(bob.sing === carl.sing); // false. 自有的sing各自持有而不是引用同一份method劫流,其實挺浪費內存空間的
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語言特性
數據類型體系
-
基本數據類型。基本數據類型對象直接分配在棧中祠汇,如數字仍秤、字符串,然后持有引用
JavaScript 中的基本數據類型(例如字符串可很、數字诗力、布爾值等)是不可變的。這意味著當你重新為一個變量賦值時我抠,實際上是在內存中創(chuàng)建了一個新的值苇本,并將變量引用這個新值。原始值在內存中的位置不會改變菜拓,但變量引用的內存地址將指向新值瓣窄。
let s = 'hello' s = 'world'
在這個例子中,我們首先將變量 s 賦值為字符串 'hello'纳鼎。當我們將 s 重新賦值為 'world' 時俺夕,實際上是創(chuàng)建了一個新的字符串 'world',并將變量 s 指向這個新字符串贱鄙。原始字符串 'hello' 仍然存在于內存中劝贸,但變量 s 現在引用的是新字符串 'world' 的內存地址。
容器類型逗宁。容器類型的對象分配在堆中映九,然后在棧中持有堆對象的引用
動態(tài)語言,變量xxx
的類型可以隨便改瞎颗,如xxx = 1
然后xxx = [1,2,3]
氯迂,這個特性決定了動態(tài)語言都要依賴引用(實際上是對指針的再封裝)。如果像golang把xxx
固定為一個int長度的內存空間言缤,那xxx
就無法再被賦值為數組了。對任何數據類型重新賦值禁灼,都是修改了引用管挟;而對引用的容器類型對象進行局部修改,不改變引用本身弄捕,整體和Python的類型機制相同僻孝。可見動態(tài)語言出于其動態(tài)特性守谓,類型系統都是一個套路
因此穿铆,js的const
,it does not define a constant value. It defines a constant reference to a value instance.
值傳遞
在 JavaScript 中斋荞,所有參數都是按值傳遞的荞雏。但是對于對象(包括數組和函數),傳遞的值本身是對象的引用,引用作為值凤优,因此它們的行為看起來像是按引用傳遞悦陋。這樣的話,如果你在函數內部修改了一個對象參數的屬性筑辨,那么在函數外部俺驶,這個屬性的原始對象也會被修改。
JavaScript 有 5 種基本的數據類型棍辕,分別是:bool暮现、null、undefined楚昭、string 和 number栖袋,還有另外三種引用類型: Array、Function 和 Object哪替。此外栋荸,在 ES6 中,引入了一種新的數據類型 - Symbol - 是一種原始數據類型凭舶,像數字或字符串一樣按值進行傳遞晌块。
總結一下,基本類型(Number帅霜、String匆背、Boolean、Undefined身冀、Null钝尸、Symbol)由于空間小、數據簡單搂根,Javascript按值傳遞(復制一份到新的棧)珍促,引用類型(Object)由于空間大、數據復雜剩愧,Javascript按共享傳遞(復制引用到新的棧)猪叙。