Node不是一門語言,只是運行環境(runtime),只是提供了基於V8引擎的運行時(運行的環境,如console),也不是js,不包含BOm,Dom,,爲了能實現服務端的功能新增了許多模塊,如https,fs,這種模塊能夠幫助咱們系統級的操做API,,使用了事件驅動(回調),非阻塞i/o(異步)的模型,包管理器npmjavascript
進程是操做系統分配資源和調度任務的基本單位php
線程是創建在進程上的一次運行單位,一個進程上能夠有多個線程css
咱們常常用的瀏覽器就是多進程的html
瀏覽器由用戶界面 -> 瀏覽器引擎(在用戶界面和呈現引擎之間傳遞信息,是瀏覽器的主進程) -> 渲染引擎前端
通常咱們先渲染css js和UI是互斥的,執行js空閒下來了就會執行cssvue
js是單線程的,不能同時操做domjava
其餘線程node
單線程特色是節約內存,而且不須要切換執行上下文,不須要管理鎖的問題webpack
瀏覽器中的 Event loopes6
webPack多線程 happyPack
worker.html
console.log(1)
let worker = new Worker('./worker.js') ; //h5內置
worker.postMessage(1000);//發消息
worker.onmessage = function(e){//收消息
console.log(e.data);
};
console.log(2)
複製代碼
worker.js
onmessage = function (e) { //收消息
let sum = 200;
this.postMessage(sum)
}
//不能操做dom
複製代碼
咱們知道js是單線程的,在stack棧裏執行,瀏覽器能夠調一些方法,,,如setTimeout屬於另外一個線程裏的了,會先進行同步代碼,將另外一個進程放到一個callback que裏面去
console.log()
setTimeout(function(){
console.log(1)
},1000)
setTimeout(function(){
console.log(1)
},5000)
複製代碼
若是調用異步代碼,不會立刻放到那個到隊列裏去,棧中的代碼執行完以後,回去callback queue(隊列中的代碼) 裏取 ,若是此時已經到1秒,就將函數去除,放到棧裏去執行,好比ajax 在成功的時候會放到隊列中,造成事件環循環,,event loop死循環 若是事件到達的時候棧中的代碼沒有執行完,就不會執行隊列中的內容
node 裏面有個應用,他會請求V8引擎 請求後回調用node API 調用完以後會跑到 LIBUV 庫, 阻塞 多線程 實現異步 當事情處理完以後經過i/o機制calback,,而後將事件放到隊列裏去,而後經過node返回給應用
let channel = new MessageChannel();
let port1 = channel.port1;
let port2 = channel.port2;
port1.postMEessage('hello')
異步代碼 vue 規定就是宏任務
port2.onMessage = function(e){
console.log(e.data)
}
複製代碼
setTimeout(()=>{
Promise.resolve('123').then(data =>{
conssole.log(3)
})
})
setTimeout(()=>{
conssole.log(3)
})
//先會執行棧中的內容,再去執行微任務,微任務清空後在執行宏任務,而後循環,宏任務會在棧中執行
複製代碼
vue 前身nextick怎麼實現的 (兼容性有問題已經被廢掉了)
let observe = new MutationOberver(function(){
console.log('dom全塞進去了')
})
//微任務
observe.observe(div,{child:true}
複製代碼
vscode 安裝插件 code runner 或者爬蟲,, 默認狀況只認js
經常使用命令
.help
.break Sometimes you get stuck, this gets you out
.clear Alias for .break
.editor Enter editor mode
.exit Exit the repl
.help Print this help message
.load Load JS from a file into the REPL session
.save Save all evaluated commands in this REPL session to a file
console.log(process.argv)
[ '/usr/local/bin/node',//node的exe文件目錄
'/Users/myloveyunyun/Desktop/node/node.js' ]//執行的文件
複製代碼
在cmd執行
咱們想拿到參數,
console.log(process.argv)
let args ={};
process.argv.slice(2).forEach((item,index)=>{
if(item.includes('--')){
args[item] = process.argv.slice(2)[index+1]
}
})
console.log(args)
複製代碼
let url;
//怎麼配置process.env.NODE_ENV
//mac export 設置環境變量 windows set 能夠根據環境變量打出對應的url
if(process.env.NODE_ENV== 'deveopment'){
url = "http://localhost:3000/api"
}else{
url = "http://baidu.com"
}
console.log(url)
複製代碼
console.log(process.cwd())
let fs = require('fs')
console.log(fs.readFileSync('./1.txt','utf8'))
[Running] node "/Users/myloveyunyun/Desktop/node/2018/node.js"
/Users/myloveyunyun/Desktop/node` `/Users/myloveyunyun/Desktop/node/2018
Error: ENOENT: no such file or directory, open './1.txt'
//這是由於當前讀取的是根目錄,咱們須要對目錄做出拼接
複製代碼
咱們能夠
process.chdir('./2018')
console.log(process.cwd())
let fs = require('fs')
console.log(fs.readFileSync('./1.txt','utf8'))
複製代碼
process.stdin.on('data',function(data){
console.log(data)
})
process.stdin.on('data',function(data){
process.stdout.write(data)
})
111
<Buffer 31 31 31 0a>
複製代碼
這個方法是異步的 process.nextTick
微任務,比宏任務快 和promise.then
比也快 兩個都是微任務,這裏額nextTick和vue裏面的nextTick不是一個東西,一個前端,一個服務端 瀏覽器不存在此方法 setTimeout 和 setImmediate 順序是不固定的
setTimeout(function(){
console.log('setTimeout')
},0)
setImmediate(function(){
console.log('setImmediate')
},0)
[Done] exited with code=0 in 0.083 seconds
[Running] node "/Users/myloveyunyun/Desktop/node/201803/node.js"
setImmediate
setTimeout
[Done] exited with code=0 in 0.066 seconds
[Running] node "/Users/myloveyunyun/Desktop/node/201803/node.js"
setTimeout
setImmediate
複製代碼
每一個步驟裏面都有一個隊列
上面的代碼咱們只看1/4/5 先走1,可是全部代碼都是異步的,node執行的時候,有準備的時間
setTimeout(()=>{
console.log('setTimeout1')
Promise.resolve('p').then(()=>{console.log('p')})
},0)
setTimeout(()=>{
console.log('setTimeout2')
},0)
//在瀏覽器裏
setTimeout1
p
setTimeout2
//node裏
[Running] node "/Users/myloveyunyun/Desktop/node/201803/node.js"
setTimeout1
setTimeout2
p
setTimeout(()=>{
console.log('setTimeout1')
},0)
setTimeout(()=>{
console.log('setTimeout2')
},0)
//從當前棧切換到隊列的瞬間執行微任務
Promise.resolve('p').then(()=>{console.log('p')})
[Running] node "/Users/myloveyunyun/Desktop/node/201803/node.js"
p
setTimeout1
setTimeout2
setImmediate(()=>{
console.log('setImmediate1');
setTimeout(()=>{
console.log('setTimeout1')
},0)
})
setTimeout(()=>{
console.log('setTimeout2');
setImmediate(()=>{
console.log('setImmediate2');
})
},0)
//三種狀況
//setTimeout2,setImmediate1,setImmediate2,setTimeout1
//setImmediate1,setTimeout2,setTimeout1,setImmediate2
//setImmediate1,setTimeout2,setImmediate2,setTimeout1
//nextTick 插孔執行,在setTimeout,setImmediate切換的時候執行,不是按方法,而是按隊列區分.且裏面不能寫遞歸,容易死循環,讓特定值在下一個隊列執行,好處是優先級高於setTimeout
fs.readFile('./1.txt','utf8',()=>{
setImmediate(()=>{
console.log('setImmediate1');
})
setTimeout(()=>{
console.log('setTimeout2');
},0)
})
//由於fs在輪詢裏,setImmediate1,setTimeout2
複製代碼
chrome://inspect
進入調試CMD 就近依賴, AMD依賴前置 eval 閉包 有關模塊化的實現請參考js模塊化
(function(exports,require,moudle,_dirname,_filename){
})()
複製代碼
fs readFile path http
package.json
若是文件夾下沒有index文件,就去這個文件查找npm init -y
初始化npm install name
node_modules
文件夾查找fs.accessSync('1.txt')//判斷文件是否存在
複製代碼
//resolve // 解析絕對路徑,傳多個參數能夠拼接
//join // 解析相對路徑,傳多個參數能夠拼接
let path = require('path');
console.log(path.resolve('./2.txt','a','b'))
console.log(path.join(__dirname,'./2.txt','a','b'))
console.log(__dirname)
console.log(__filename)
[Running] node "/Users/myloveyunyun/Desktop/node/201803/node.js"
/Users/myloveyunyun/Desktop/node/2.txt/a/b
/Users/myloveyunyun/Desktop/node/201803/2.txt/a/b
/Users/myloveyunyun/Desktop/node/201803
/Users/myloveyunyun/Desktop/node/201803/node.js
console.log(path.extname('1.a.d.f')) // 後最
console.log(path.basename('1.a.s.f.g','.g')) //基礎名字
console.log(path.sep)//環境變量分隔符
console.log(path.posix.sep)//mac下分隔符 /windows \
console.log(path.delimiter)//mac下; /windows:
[Running] node "/Users/myloveyunyun/Desktop/node/201803/node.js"
.f
1.a.s.f
/
;
複製代碼
let vm = require('vm');
let a = 'sd'
vm.runInThisContext('console.log(a)') //沙箱
eval('console.log(a)'),會查找
ReferenceError: a is not defined
at evalmachine.<anonymous>:1:1
複製代碼
let vm = require('vm');
let a = 'sd'
vm.runInThisContext('console.log(a)') //沙箱
eval('console.log(a)')//會查找
ReferenceError: a is not defined
at evalmachine.<anonymous>:1:1
複製代碼
ConmonJS規範
下面咱們根據以上三個特色,考慮各類狀況,一步步實現一個簡單的CommenJS引入功能
首先咱們要引入所用到的模塊
a. 讀文件
b. 獲取文件路徑
c. 運行環境
d. 加載策略=>針對js/json/node文件
e. Module
f. 緩存
g. requier方法
let fs = require('fs');
let path = require('path');
let vm = require('vm');
function Module(p) {
this.id = p; // 當前模塊的標識
this.exports = {}; // 每一個模塊都有一個exports屬性
this.loaded = false; // 這個模塊默認沒有加載完
}
Module._extensions = {
//js優先級高於json,和node
'.js': function (Module) {},
'.json': function (Module) {},
'.node': 'xxx'
}
Module._cacheModule = {}// 根據的是絕對路徑進行緩存的
function require(moduleId){//咱們將加載模塊以參數形式傳遞過來
}
複製代碼
以上是咱們讀取模塊必備的條件,下面咱們挨個增長內容, 2. 在require接收到路徑的時候,咱們首先要對此路徑作解析,假設咱們給個方法_resolveFileName(moduleId)
對路徑做出解析
// 解析絕對路徑的方法 返回一個絕對路徑
Module._resolveFileName = function (moduleId) {
let p = path.resolve(moduleId);
// 沒有後綴在加上後綴 若是傳過來的有後綴就不用加了
if (!path.extname(moduleId)) {//extname是path內部方法,在這裏用到的相似用法,清自行查閱,筆者就先很少作解釋了
//keys將一個對象轉成數組
let arr = Object.keys(Module._extensions);
//若是沒有後蕞名稱,由於只有三種狀況,咱們挨個對比,在這裏是有前後順序的,假設傳過來a,咱們會現識別a.js
for (let i = 0; i < arr.length; i++) {
let file = p + arr[i];
try {
fs.accessSync(file);//accessSync 同步斷定分揀是否存在
return file;
} catch (e) {
console.log(e)
}
}
} else {
return p;//若是有後墜就直接查找此文件,無需匹配
}
}
function req(moduleId) {
let p = Module._resolveFileName(moduleId);// p是一個絕對路徑
}
複製代碼
Module._extensions = {
//js優先級高於json,和node
'.js': function (Module) {},//這裏的function指的是加載該類型文件的方法
'.json': function (Module) {},
'.node': 'xxx'
}
function req(moduleId) {
let p = Module._resolveFileName(moduleId);// p是一個絕對路徑
if (Module._cacheModule[p]) {
// 模塊存在,若是有直接把對象返回便可稍後補充
}
// 表示沒有緩存就生成一個模塊
let module = new Module(p);
// 加載模塊
let content = module.load(p); // 加載模塊
Module._cacheModule[p] = module;
module.exports = content; //最終以module.export導出
return module.exports
}
複製代碼
在此過程當中 ,咱們生成一個模塊,new了一個moudle
, 將路徑傳過去,還記得上面的代碼,在new的過程當中,咱們給模塊加了id,exports,load
,而後我加載此模塊,而且將它添加到緩存中 load
方法是在實例上調用的,咱們將吧這個方法寫在Module
的原型上
//在new以後就有了這些標識
function Module(p) {
this.id = p; // 當前模塊的標識
this.exports = {}; // 每一個模塊都有一個exports屬性
this.loaded = false; // 這個模塊默認沒有加載完
}
Module.prototype.load = function (filepath) {
//判斷加載的文件是json仍是node,仍是js
let ext = path.extname(filepath);
//根據文件類型添加方法
let content = Module._extensions[ext](this); //成功讀取文件內容
//this指當前模塊的實例 有id exports loaded
return content;
}
複製代碼
//exports,require,module
Module.warpper = ['(function(exports,require,module){', '\n})'];
Module._extensions = {
//js優先級高於json,和node
'.js': function (Module) {
let script = fs.readFileSync(module.id, 'utf8');
let fn = Module.warpper[0] + script + Module.warpper[1];
//咱們須要執行此文件,可是eval會在當前做用域向上查找,咱們只想在require以後執行此文件,所以這裏使用沙漏限制環境
vm.runInThisContext(fn).call(Module.exports, Module.exports, req, module)
return module.exports;
},
'.json': function (Module) {
return JSON.parse(fs.readFileSync(module.id, 'utf8')); // 讀取那個文件
},
'.node': 'xxx'
}
複製代碼
這樣咱們的基本功能就實現了
// 什麼是commonjs規範
// 定義瞭如何導入模塊 require
// 還定義瞭如何導出模塊 module.exports 導出xxx
// 還定義了一個js就是一個模塊
let fs = require('fs');
let path = require('path');
let vm = require('vm');
//解析絕對路徑方法,返回絕對路徑
//因爲es6不支持靜態屬性,咱們暫時用es5實現
//全部加載策略
function Module(p) {
this.id = p; // 當前模塊的標識
this.exports = {}; // 每一個模塊都有一個exports屬性
this.loaded = false; // 這個模塊默認沒有加載完
}
// 全部的加載策略
Module.warpper = ['(function(exports,require,module){', '\n})'];
Module._extensions = {
//js優先級高於json,和node
'.js': function (Module) {
let script = fs.readFileSync(module.id, 'utf8');
let fn = Module.warpper[0] + script + Module.warpper[1];
vm.runInThisContext(fn).call(Module.exports, Module.exports, req, module)
return module.exports;
},
'.json': function (Module) {
return JSON.parse(fs.readFileSync(module.id, 'utf8')); // 讀取那個文件
},
'.node': 'xxx'
}
//根據絕對路徑進行緩存
Module._cacheModule = {};
Module.resolveFileName = function (ModuleId) {
let p = path.join(ModuleId);
//若是後最不存在則查找
if (!path.extname(ModuleId)) {
//keys將一個對象轉成數組
let arr = Object.keys(Module._extensions);
for (let i = 0; i < arr.length; i++) {
let file = p + arr[i];
//模塊加載的整個過程都是同步的
try {
fs.accessSync(file);
return file;
} catch (e) {
console.log(e)
}
}
} else {
return p;
}
}
Module.prototype.load = function (filepath) {
//判斷加載的文件是json仍是node,仍是js
let ext = path.extname(filepath)
let content = Module._extensions[ext](this); //成功讀取文件內容
//this指當前模塊的實例 有id exports loaded
return content
}
function req(ModuleId) {
//解析絕對路徑,
//且判斷文件類型json,js,node
//得倒真實路徑去查找緩存
let p = Module.resolveFileName(ModuleId); //p是一個絕對路徑
if (Module._cacheModule[p]) {
// 模塊不存在,若是有直接把exports對象返回便可
return Module._cacheModule[p].exports;
}
//沒有緩存則建立一個模塊
let moudule = new Module(p);
let content = module.load(p);
Module._cacheModule[p] = module;
module.exports = content;
return module.exports;
//加載模塊
}
let a = req('./a.js');
req('./a.js');
console.log(a);
複製代碼
對於文件模塊,若是js,json,node都不存在其實會找文件夾下的package.json,若是json文件沒有,會找該文件夾下的index,在這裏咱們不作過多的闡述。 文件模塊查找規則