国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

AST抽象語法樹——最基礎(chǔ)的javascript重點(diǎn)知識(shí),99%的人根本不了解

godiscoder / 861人閱讀

摘要:抽象語法樹,是一個(gè)非常基礎(chǔ)而重要的知識(shí)點(diǎn),但國(guó)內(nèi)的文檔卻幾乎一片空白。事實(shí)上,在世界中,你可以認(rèn)為抽象語法樹是最底層。通過抽象語法樹解析,我們可以像童年時(shí)拆解玩具一樣,透視這臺(tái)機(jī)器的運(yùn)轉(zhuǎn),并且重新按著你的意愿來組裝。

抽象語法樹(AST),是一個(gè)非常基礎(chǔ)而重要的知識(shí)點(diǎn),但國(guó)內(nèi)的文檔卻幾乎一片空白。

本文將帶大家從底層了解AST,并且通過發(fā)布一個(gè)小型前端工具,來帶大家了解AST的強(qiáng)大功能

Javascript就像一臺(tái)精妙運(yùn)作的機(jī)器,我們可以用它來完成一切天馬行空的構(gòu)思。

我們對(duì)javascript生態(tài)了如指掌,卻常忽視javascript本身。這臺(tái)機(jī)器,究竟是哪些零部件在支持著它運(yùn)行?

AST在日常業(yè)務(wù)中也許很難涉及到,但當(dāng)你不止于想做一個(gè)工程師,而想做工程師的工程師,寫出vue、react之類的大型框架,或類似webpack、vue-cli前端自動(dòng)化的工具,或者有批量修改源碼的工程需求,那你必須懂得AST。AST的能力十分強(qiáng)大,且能幫你真正吃透javascript的語言精髓。

事實(shí)上,在javascript世界中,你可以認(rèn)為抽象語法樹(AST)是最底層。 再往下,就是關(guān)于轉(zhuǎn)換和編譯的“黑魔法”領(lǐng)域了。

人生第一次拆解Javascript

小時(shí)候,當(dāng)我們拿到一個(gè)螺絲刀和一臺(tái)機(jī)器,人生中最令人懷念的夢(mèng)幻時(shí)刻便開始了:

我們把機(jī)器,拆成一個(gè)一個(gè)小零件,一個(gè)個(gè)齒輪與螺釘,用巧妙的機(jī)械原理銜接在一起...

當(dāng)我們把它重新照不同的方式組裝起來,這時(shí),機(jī)器重新又跑動(dòng)了起來——世界在你眼中如獲新生。

通過抽象語法樹解析,我們可以像童年時(shí)拆解玩具一樣,透視Javascript這臺(tái)機(jī)器的運(yùn)轉(zhuǎn),并且重新按著你的意愿來組裝。

現(xiàn)在,我們拆解一個(gè)簡(jiǎn)單的add函數(shù)

function add(a, b) {
    return a + b
}

首先,我們拿到的這個(gè)語法塊,是一個(gè)FunctionDeclaration(函數(shù)定義)對(duì)象。

用力拆開,它成了三塊:

一個(gè)id,就是它的名字,即add

兩個(gè)params,就是它的參數(shù),即[a, b]

一塊body,也就是大括號(hào)內(nèi)的一堆東西

add沒辦法繼續(xù)拆下去了,它是一個(gè)最基礎(chǔ)Identifier(標(biāo)志)對(duì)象,用來作為函數(shù)的唯一標(biāo)志,就像人的姓名一樣。

{
    name: "add"
    type: "identifier"
    ...
}

params繼續(xù)拆下去,其實(shí)是兩個(gè)Identifier組成的數(shù)組。之后也沒辦法拆下去了。

[
    {
        name: "a"
        type: "identifier"
        ...
    },
    {
        name: "b"
        type: "identifier"
        ...
    }
]

接下來,我們繼續(xù)拆開body
我們發(fā)現(xiàn),body其實(shí)是一個(gè)BlockStatement(塊狀域)對(duì)象,用來表示是{return a + b}

打開Blockstatement,里面藏著一個(gè)ReturnStatement(Return域)對(duì)象,用來表示return a + b

繼續(xù)打開ReturnStatement,里面是一個(gè)BinaryExpression(二項(xiàng)式)對(duì)象,用來表示a + b

繼續(xù)打開BinaryExpression,它成了三部分,leftoperatorright

operator+

left 里面裝的,是Identifier對(duì)象 a

right 里面裝的,是Identifer對(duì)象 b

就這樣,我們把一個(gè)簡(jiǎn)單的add函數(shù)拆解完畢,用圖表示就是

看!抽象語法樹(Abstract Syntax Tree),的確是一種標(biāo)準(zhǔn)的樹結(jié)構(gòu)。

那么,上面我們提到的Identifier、Blockstatement、ReturnStatement、BinaryExpression, 這一個(gè)個(gè)小部件的說明書去哪查?

請(qǐng)查看 AST對(duì)象文檔

送給你的AST螺絲刀:recast

輸入命令:

npm i recast -S

你即可獲得一把操縱語法樹的螺絲刀

接下來,你可以在任意js文件下操縱這把螺絲刀,我們新建一個(gè)parse.js示意:

parse.js

// 給你一把"螺絲刀"——recast
const recast = require("recast");

// 你的"機(jī)器"——一段代碼
// 我們使用了很奇怪格式的代碼,想測(cè)試是否能維持代碼結(jié)構(gòu)
const code =
  `
  function add(a, b) {
    return a +
      // 有什么奇怪的東西混進(jìn)來了
      b
  }
  `
// 用螺絲刀解析機(jī)器
const ast = recast.parse(code);

// ast可以處理很巨大的代碼文件
// 但我們現(xiàn)在只需要代碼塊的第一個(gè)body,即add函數(shù)
const add  = ast.program.body[0]

console.log(add)

輸入node parse.js你可以查看到add函數(shù)的結(jié)構(gòu),與之前所述一致,通過AST對(duì)象文檔可查到它的具體屬性:

FunctionDeclaration{
    type: "FunctionDeclaration",
    id: ...
    params: ...
    body: ...
}

你也可以繼續(xù)使用console.log透視它的更內(nèi)層,如:

console.log(add.params[0])
console.log(add.body.body[0].argument.left)
recast.types.builders 制作模具

一個(gè)機(jī)器,你只會(huì)拆開重裝,不算本事。

拆開了,還能改裝,才算上得了臺(tái)面。

recast.types.builders里面提供了不少“模具”,讓你可以輕松地拼接成新的機(jī)器。

最簡(jiǎn)單的例子,我們想把之前的function add(a, b){...}聲明,改成匿名函數(shù)式聲明const add = function(a ,b){...}

如何改裝?

第一步,我們創(chuàng)建一個(gè)VariableDeclaration變量聲明對(duì)象,聲明頭為const, 內(nèi)容為一個(gè)即將創(chuàng)建的VariableDeclarator對(duì)象。

第二步,創(chuàng)建一個(gè)VariableDeclarator,放置add.id在左邊, 右邊是將創(chuàng)建的FunctionDeclaration對(duì)象

第三步,我們創(chuàng)建一個(gè)FunctionDeclaration,如前所述的三個(gè)組件,id params body中,因?yàn)槭悄涿瘮?shù)id設(shè)為空,params使用add.params,body使用add.body。

這樣,就創(chuàng)建好了const add = function(){}的AST對(duì)象。

在之前的parse.js代碼之后,加入以下代碼

// 引入變量聲明,變量符號(hào),函數(shù)聲明三種“模具”
const {variableDeclaration, variableDeclarator, functionExpression} = recast.types.builders

// 將準(zhǔn)備好的組件置入模具,并組裝回原來的ast對(duì)象。
ast.program.body[0] = variableDeclaration("const", [
  variableDeclarator(add.id, functionExpression(
    null, // Anonymize the function expression.
    add.params,
    add.body
  ))
]);

//將AST對(duì)象重新轉(zhuǎn)回可以閱讀的代碼
const output = recast.print(ast).code;

console.log(output)

可以看到,我們打印出了

const add = function(a, b) {
  return a +
    // 有什么奇怪的東西混進(jìn)來了
    b
};

最后一行

const output = recast.print(ast).code;

其實(shí)是recast.parse的逆向過程,具體公式為

recast.print(recast.parse(source)).code === source

打印出來還保留著“原裝”的函數(shù)內(nèi)容,連注釋都沒有變。

我們其實(shí)也可以打印出美化格式的代碼段:

const output = recast.prettyPrint(ast, { tabWidth: 2 }).code

輸出為

const add = function(a, b) {
  return a + b;
};
現(xiàn)在,你是不是已經(jīng)產(chǎn)生了“我可以通過AST樹生成任何js代碼”的幻覺?

我鄭重告訴你,這不是幻覺。

實(shí)戰(zhàn)進(jìn)階:命令行修改js文件

除了parse/print/builder以外,Recast的三項(xiàng)主要功能:

run: 通過命令行讀取js文件,并轉(zhuǎn)化成ast以供處理。

tnt: 通過assert()和check(),可以驗(yàn)證ast對(duì)象的類型。

visit: 遍歷ast樹,獲取有效的AST對(duì)象并進(jìn)行更改。

我們通過一個(gè)系列小務(wù)來學(xué)習(xí)全部的recast工具庫:

創(chuàng)建一個(gè)用來示例文件,假設(shè)是demo.js

demo.js

function add(a, b) {
  return a + b
}

function sub(a, b) {
  return a - b
}

function commonDivision(a, b) {
  while (b !== 0) {
    if (a > b) {
      a = sub(a, b)
    } else {
      b = sub(b, a)
    }
  }
  return a
}
recast.run —— 命令行文件讀取

新建一個(gè)名為read.js的文件,寫入
read.js

recast.run( function(ast, printSource){
    printSource(ast)
})

命令行輸入

node read demo.js

我們查以看到j(luò)s文件內(nèi)容打印在了控制臺(tái)上。

我們可以知道,node read可以讀取demo.js文件,并將demo.js內(nèi)容轉(zhuǎn)化為ast對(duì)象。

同時(shí)它還提供了一個(gè)printSource函數(shù),隨時(shí)可以將ast的內(nèi)容轉(zhuǎn)換回源碼,以方便調(diào)試。

recast.visit —— AST節(jié)點(diǎn)遍歷

read.js

#!/usr/bin/env node
const recast  = require("recast")

recast.run(function(ast, printSource) {
  recast.visit(ast, {
      visitExpressionStatement: function({node}) {
        console.log(node)
        return false
      }
    });
});

recast.visit將AST對(duì)象內(nèi)的節(jié)點(diǎn)進(jìn)行逐個(gè)遍歷。

注意

你想操作函數(shù)聲明,就使用visitFunctionDelaration遍歷,想操作賦值表達(dá)式,就使用visitExpressionStatement。 只要在 AST對(duì)象文檔中定義的對(duì)象,在前面加visit,即可遍歷。

通過node可以取到AST對(duì)象

每個(gè)遍歷函數(shù)后必須加上return false,或者選擇以下寫法,否則報(bào)錯(cuò):

#!/usr/bin/env node
const recast  = require("recast")

recast.run(function(ast, printSource) {
  recast.visit(ast, {
      visitExpressionStatement: function(path) {
        const node = path.node
        printSource(node)
        this.traverse(path)
      }
    })
});

調(diào)試時(shí),如果你想輸出AST對(duì)象,可以console.log(node)

如果你想輸出AST對(duì)象對(duì)應(yīng)的源碼,可以printSource(node)

命令行輸入`
node read demo.js`進(jìn)行測(cè)試。

#!/usr/bin/env node 在所有使用recast.run()的文件頂部都需要加入這一行,它的意義我們最后再討論。
TNT —— 判斷AST對(duì)象類型

TNT,即recast.types.namedTypes,就像它的名字一樣火爆,它用來判斷AST對(duì)象是否為指定的類型。

TNT.Node.assert(),就像在機(jī)器里埋好的炸藥,當(dāng)機(jī)器不能完好運(yùn)轉(zhuǎn)時(shí)(類型不匹配),就炸毀機(jī)器(報(bào)錯(cuò)退出)

TNT.Node.check(),則可以判斷類型是否一致,并輸出False和True

上述Node可以替換成任意AST對(duì)象,例如TNT.ExpressionStatement.check(),TNT.FunctionDeclaration.assert()

read.js

#!/usr/bin/env node
const recast = require("recast");
const TNT = recast.types.namedTypes

recast.run(function(ast, printSource) {
  recast.visit(ast, {
      visitExpressionStatement: function(path) {
        const node = path.value
        // 判斷是否為ExpressionStatement,正確則輸出一行字。
        if(TNT.ExpressionStatement.check(node)){
          console.log("這是一個(gè)ExpressionStatement")
        }
        this.traverse(path);
      }
    });
});

read.js

#!/usr/bin/env node
const recast = require("recast");
const TNT = recast.types.namedTypes

recast.run(function(ast, printSource) {
  recast.visit(ast, {
      visitExpressionStatement: function(path) {
        const node = path.node
        // 判斷是否為ExpressionStatement,正確不輸出,錯(cuò)誤則全局報(bào)錯(cuò)
        TNT.ExpressionStatement.assert(node)
        this.traverse(path);
      }
    });
});

命令行輸入`
node read demo.js`進(jìn)行測(cè)試。

實(shí)戰(zhàn):用AST修改源碼,導(dǎo)出全部方法

exportific.js

現(xiàn)在,我們想讓這個(gè)文件中的函數(shù)改寫成能夠全部導(dǎo)出的形式,例如

function add (a, b) {
    return a + b
}

想改變?yōu)?/p>

exports.add = (a, b) => {
  return a + b
}

除了使用fs.read讀取文件、正則匹配替換文本、fs.write寫入文件這種笨拙的方式外,我們可以用AST優(yōu)雅地解決問題

查詢AST對(duì)象文檔

首先,我們先用builders憑空實(shí)現(xiàn)一個(gè)鍵頭函數(shù)

exportific.js

#!/usr/bin/env node
const recast = require("recast");
const {
  identifier:id,
  expressionStatement,
  memberExpression,
  assignmentExpression,
  arrowFunctionExpression,
  blockStatement
} = recast.types.builders

recast.run(function(ast, printSource) {
  // 一個(gè)塊級(jí)域 {}
  console.log("

step1:")
  printSource(blockStatement([]))

  // 一個(gè)鍵頭函數(shù) ()=>{}
  console.log("

step2:")
  printSource(arrowFunctionExpression([],blockStatement([])))

  // add賦值為鍵頭函數(shù)  add = ()=>{}
  console.log("

step3:")
  printSource(assignmentExpression("=",id("add"),arrowFunctionExpression([],blockStatement([]))))

  // exports.add賦值為鍵頭函數(shù)  exports.add = ()=>{}
  console.log("

step4:")
  printSource(expressionStatement(assignmentExpression("=",memberExpression(id("exports"),id("add")),
    arrowFunctionExpression([],blockStatement([])))))
});

上面寫了我們一步一步推斷出exports.add = ()=>{}的過程,從而得到具體的AST結(jié)構(gòu)體。

使用node exportific demo.js運(yùn)行可查看結(jié)果。

接下來,只需要在獲得的最終的表達(dá)式中,把id("add")替換成遍歷得到的函數(shù)名,把參數(shù)替換成遍歷得到的函數(shù)參數(shù),把blockStatement([])替換為遍歷得到的函數(shù)塊級(jí)作用域,就成功地改寫了所有函數(shù)!

另外,我們需要注意,在commonDivision函數(shù)內(nèi),引用了sub函數(shù),應(yīng)改寫成exports.sub

exportific.js

#!/usr/bin/env node
const recast = require("recast");
const {
  identifier: id,
  expressionStatement,
  memberExpression,
  assignmentExpression,
  arrowFunctionExpression
} = recast.types.builders

recast.run(function (ast, printSource) {
  // 用來保存遍歷到的全部函數(shù)名
  let funcIds = []
  recast.types.visit(ast, {
    // 遍歷所有的函數(shù)定義
    visitFunctionDeclaration(path) {
      //獲取遍歷到的函數(shù)名、參數(shù)、塊級(jí)域
      const node = path.node
      const funcName = node.id
      const params = node.params
      const body = node.body

      // 保存函數(shù)名
      funcIds.push(funcName.name)
      // 這是上一步推導(dǎo)出來的ast結(jié)構(gòu)體
      const rep = expressionStatement(assignmentExpression("=", memberExpression(id("exports"), funcName),
        arrowFunctionExpression(params, body)))
      // 將原來函數(shù)的ast結(jié)構(gòu)體,替換成推導(dǎo)ast結(jié)構(gòu)體
      path.replace(rep)
      // 停止遍歷
      return false
    }
  })


  recast.types.visit(ast, {
    // 遍歷所有的函數(shù)調(diào)用
    visitCallExpression(path){
      const node = path.node;
      // 如果函數(shù)調(diào)用出現(xiàn)在函數(shù)定義中,則修改ast結(jié)構(gòu)
      if (funcIds.includes(node.callee.name)) {
        node.callee = memberExpression(id("exports"), node.callee)
      }
      // 停止遍歷
      return false
    }
  })
  // 打印修改后的ast源碼
  printSource(ast)
})
一步到位,發(fā)一個(gè)最簡(jiǎn)單的exportific前端工具

上面講了那么多,仍然只體現(xiàn)在理論階段。

但通過簡(jiǎn)單的改寫,就能通過recast制作成一個(gè)名為exportific的源碼編輯工具。

以下代碼添加作了兩個(gè)小改動(dòng)

添加說明書--help,以及添加了--rewrite模式,可以直接覆蓋文件或默認(rèn)為導(dǎo)出*.export.js文件。

將之前代碼最后的 printSource(ast)替換成 writeASTFile(ast,filename,rewriteMode)

exportific.js

#!/usr/bin/env node
const recast = require("recast");
const {
  identifier: id,
  expressionStatement,
  memberExpression,
  assignmentExpression,
  arrowFunctionExpression
} = recast.types.builders

const fs = require("fs")
const path = require("path")
// 截取參數(shù)
const options = process.argv.slice(2)

//如果沒有參數(shù),或提供了-h 或--help選項(xiàng),則打印幫助
if(options.length===0 || options.includes("-h") || options.includes("--help")){
  console.log(`
    采用commonjs規(guī)則,將.js文件內(nèi)所有函數(shù)修改為導(dǎo)出形式。

    選項(xiàng): -r  或 --rewrite 可直接覆蓋原有文件
    `)
  process.exit(0)
}

// 只要有-r 或--rewrite參數(shù),則rewriteMode為true
let rewriteMode = options.includes("-r") || options.includes("--rewrite")

// 獲取文件名
const clearFileArg = options.filter((item)=>{
  return !["-r","--rewrite","-h","--help"].includes(item)
})

// 只處理一個(gè)文件
let filename = clearFileArg[0]

const writeASTFile = function(ast, filename, rewriteMode){
  const newCode = recast.print(ast).code
  if(!rewriteMode){
    // 非覆蓋模式下,將新文件寫入*.export.js下
    filename = filename.split(".").slice(0,-1).concat(["export","js"]).join(".")
  }
  // 將新代碼寫入文件
  fs.writeFileSync(path.join(process.cwd(),filename),newCode)
}


recast.run(function (ast, printSource) {
  let funcIds = []
  recast.types.visit(ast, {
    visitFunctionDeclaration(path) {
      //獲取遍歷到的函數(shù)名、參數(shù)、塊級(jí)域
      const node = path.node
      const funcName = node.id
      const params = node.params
      const body = node.body

      funcIds.push(funcName.name)
      const rep = expressionStatement(assignmentExpression("=", memberExpression(id("exports"), funcName),
        arrowFunctionExpression(params, body)))
      path.replace(rep)
      return false
    }
  })


  recast.types.visit(ast, {
    visitCallExpression(path){
      const node = path.node;
      if (funcIds.includes(node.callee.name)) {
        node.callee = memberExpression(id("exports"), node.callee)
      }
      return false
    }
  })

  writeASTFile(ast,filename,rewriteMode)
})

現(xiàn)在嘗試一下

node exportific demo.js

已經(jīng)可以在當(dāng)前目錄下找到源碼變更后的demo.export.js文件了。

npm發(fā)包

編輯一下package.json文件

{
  "name": "exportific",
  "version": "0.0.1",
  "description": "改寫源碼中的函數(shù)為可exports.XXX形式",
  "main": "exportific.js",
  "bin": {
    "exportific": "./exportific.js"
  },
  "keywords": [],
  "author": "wanthering",
  "license": "ISC",
  "dependencies": {
    "recast": "^0.15.3"
  }
}

注意bin選項(xiàng),它的意思是將全局命令exportific指向當(dāng)前目錄下的exportific.js

這時(shí),輸入npm link 就在本地生成了一個(gè)exportific命令。

之后,只要哪個(gè)js文件想導(dǎo)出來使用,就exportific XXX.js一下。

這是在本地的玩法,想和大家一起分享這個(gè)前端小工具,只需要發(fā)布npm包就行了。

同時(shí),一定要注意exportific.js文件頭有

#!/usr/bin/env node

否則在使用時(shí)將報(bào)錯(cuò)。

接下來,正式發(fā)布npm包!

如果你已經(jīng)有了npm 帳號(hào),請(qǐng)使用npm login登錄

如果你還沒有npm帳號(hào) https://www.npmjs.com/signup 非常簡(jiǎn)單就可以注冊(cè)npm

然后,輸入
npm publish

沒有任何繁瑣步驟,絲毫審核都沒有,你就發(fā)布了一個(gè)實(shí)用的前端小工具exportific 。任何人都可以通過

npm i exportific -g

全局安裝這一個(gè)插件。

提示:==在試驗(yàn)教程時(shí),請(qǐng)不要和我的包重名,修改一下發(fā)包名稱。==

結(jié)語

我們對(duì)javascript再熟悉不過,但透過AST的視角,最普通的js語句,卻煥發(fā)出精心動(dòng)魄的美感。你可以通過它批量構(gòu)建任何javascript代碼!

童年時(shí),這個(gè)世界充滿了新奇的玩具,再普通的東西在你眼中都如同至寶。如今,計(jì)算機(jī)語言就是你手中的大玩具,一段段AST對(duì)象的拆分組裝,構(gòu)建出我們所生活的網(wǎng)絡(luò)世界。

所以不得不說軟件工程師是一個(gè)幸福的工作,你心中住的仍然是那個(gè)午后的少年,永遠(yuǎn)有無數(shù)新奇等你發(fā)現(xiàn),永遠(yuǎn)有無數(shù)夢(mèng)想等你構(gòu)建。

github地址:https://github.com/wanthering...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/97249.html

相關(guān)文章

  • 通過實(shí)現(xiàn)一個(gè)簡(jiǎn)易打包工具,分析打包核心原理

    摘要:而在編譯過程中通過語法和詞法的分析得出一顆語法樹,我們可以將它稱為抽象語法樹也稱為語法樹,指的是源代碼語法所對(duì)應(yīng)的樹狀結(jié)構(gòu)。而這個(gè)卻恰恰使我們分析打包工具的重點(diǎn)核心。 概述 眼下wepack似乎已經(jīng)成了前端開發(fā)中不可缺少的工具之一,而他的一切皆模塊的思想隨著webpack版本不斷的迭代(webpack 4)使其打包速度更快,效率更高的為我們的前端工程化服務(wù)showImg(https:/...

    red_bricks 評(píng)論0 收藏0
  • 探索babel和babel插件是怎么工作

    摘要:我們更多要去做的是去修改和改變生成的這個(gè)抽象語法樹。我們已經(jīng)知道會(huì)遍歷節(jié)點(diǎn)組成的抽象語法樹,每一個(gè)節(jié)點(diǎn)都會(huì)有自己對(duì)應(yīng)的比如變量節(jié)點(diǎn)等。 你有可能會(huì)聽到過這個(gè)詞 webpack工程師 ,這個(gè)看似像是一個(gè)專業(yè)很強(qiáng)的職位其實(shí)很多時(shí)候是一些前端對(duì)現(xiàn)在前端工作方式對(duì)一些吐槽,對(duì)于一個(gè)之前沒有接觸過webpack,nodejs,babel 之類的工具的人來說,看到大量的配置文件后很多人都會(huì)看懵 s...

    dongxiawu 評(píng)論0 收藏0
  • 校招社招必備核心前端面試問題與詳細(xì)解答

    摘要:本文總結(jié)了前端老司機(jī)經(jīng)常問題的一些問題并結(jié)合個(gè)人總結(jié)給出了比較詳盡的答案。網(wǎng)易阿里騰訊校招社招必備知識(shí)點(diǎn)。此外還有網(wǎng)絡(luò)線程,定時(shí)器任務(wù)線程,文件系統(tǒng)處理線程等等。線程核心是引擎。主線程和工作線程之間的通知機(jī)制叫做事件循環(huán)。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文總結(jié)了前端老司機(jī)經(jīng)常問題的一些問題并結(jié)合個(gè)...

    jonh_felix 評(píng)論0 收藏0
  • 校招社招必備核心前端面試問題與詳細(xì)解答

    摘要:本文總結(jié)了前端老司機(jī)經(jīng)常問題的一些問題并結(jié)合個(gè)人總結(jié)給出了比較詳盡的答案。網(wǎng)易阿里騰訊校招社招必備知識(shí)點(diǎn)。此外還有網(wǎng)絡(luò)線程,定時(shí)器任務(wù)線程,文件系統(tǒng)處理線程等等。線程核心是引擎。主線程和工作線程之間的通知機(jī)制叫做事件循環(huán)。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文總結(jié)了前端老司機(jī)經(jīng)常問題的一些問題并結(jié)合個(gè)...

    Rango 評(píng)論0 收藏0
  • 如何編寫簡(jiǎn)單parser(基礎(chǔ)篇)

    摘要:在這里,詞法解析器應(yīng)用的規(guī)則即為詞匯語法的定義,語法解釋器應(yīng)用的規(guī)則即為表達(dá)式語句聲明和函數(shù)等的定義。如何編寫簡(jiǎn)單的實(shí)踐篇 什么是parser? 簡(jiǎn)單的說,parser的工作即是將代碼片段轉(zhuǎn)換成計(jì)算機(jī)可讀的數(shù)據(jù)結(jié)構(gòu)的過程。這個(gè)計(jì)算機(jī)可讀的數(shù)據(jù)結(jié)構(gòu)更專業(yè)的說法是抽象語法樹(abstract syntax tree),簡(jiǎn)稱AST。AST是代碼片段具體語義的抽象表達(dá),它不包含該段代碼的所有細(xì)...

    Barry_Ng 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<