JavaScript 元編程

大家好,我是若川。今天給分享一篇來自freecodecamp的好文。我是freecodecamp杭州社區組織者之一,有一群小伙伴一起組織線下分享活動,不過2020年我們杭州社區幾乎沒有活躍,我也沒有什么貢獻。

另外,我的公眾號「若川視野」,也可以是你們的舞臺。如果寫了好文章也可以投稿到我的公眾號。

以下是正文~


JavaScript 有許多開發者熟知的有用特性,同時也有一些鮮為人知的特性能夠幫助我們解決棘手問題。

很多人可能不太了解 JavaScript 元編程的概念,本文會介紹元編程的知識和它的作用。

ES6(ECMAScript 2015)新增了對 ?Reflect ?和 ?Proxy ?對象的支持,使得我們能夠便捷地進行元編程。讓我們通過示例來學習它們的用法。

什么是元編程?

元編程 ?無異于 ?編程中的魔法!如果編寫一個“能夠讀取、修改、分析、甚至生成新程序”的程序將會如何?是不是聽起來很神奇、很強大?

元編程很神奇

維基百科這樣描述元編程:

元編程 ?是一種編程技術,編寫出來的計算機程序能夠將其他程序作為數據來處理。意味著可以編寫出這樣的程序:它能夠讀取、生成、分析或者轉換其它程序,甚至在運行時修改程序自身。

簡而言之,元編程能夠寫出這樣的代碼:

  • 可以生成代碼

  • 可以在運行時修改語言結構,這種現象被稱為 ?反射編程 ?或 ?反射

什么是反射?

反射 ?是元編程的一個分支。反射又有三個子分支:

  1. 自省(Introspection):代碼能夠自我檢查、訪問內部屬性,我們可以據此獲得代碼的底層信息。

  2. 自我修改(Self-Modification):顧名思義,代碼可以修改自身。

  3. 調解(Intercession):字面意思是“代他人行事”。在元編程中,調解的概念類似于包裝(wrapping)、捕獲(trapping)、攔截(intercepting)。

ES6 為我們提供了 ?Reflect ?對象(Reflect API)來實現 ?自省,還提供了 ?Proxy ?對象幫助我們實現 ?調解。我們要盡力避免 ?自我修改,所以本文不會過多談及這一點。

需要說明的是,元編程并不是由 ES6 引入的,JavaScript 語言從一開始就支持元編程,ES6 只是讓它變得更易于使用。

ES6 之前的元編程

還記得 ?eval ?嗎?看看它的用法:

const?blog?=?{name:?'freeCodeCamp'
}
console.log('Before?eval:',?blog);const?key?=?'author';
const?value?=?'Tapas';
testEval?=?()?=>?eval(`blog.${key}?=?'${value}'`);//?調用函數
testEval();console.log('After?eval?magic:',?blog);

eval ?生成了額外的代碼。示例代碼執行時為 ?blog ?對象增加了一個 ?author ?屬性。

Before?eval:?{name:?freeCodeCamp}
After?eval?magic:?{name:?"freeCodeCamp",?author:?"Tapas"}

自省

在 ES6 引入 ?Reflect 對象 ?之前,我們也可以實現自省。下面是讀取程序結構的示例:

var?users?=?{'Tom':?32,'Bill':?50,'Sam':?65
};Object.keys(users).forEach(name?=>?{const?age?=?users[name];console.log(`User?${name}?is?${age}?years?old!`);
});

我們讀取了 ?users ?對象的結構并以鍵值對的形式打印出來。

User?Tom?is?32?years?old!
User?Bill?is?50?years?old!
User?Sam?is?65?years?old!

自我修改

以一個包含修改其自身的方法的 ?blog ?對象為例:

var?blog?=?{name:?'freeCodeCamp',modifySelf:?function(key,?value)?{blog[key]?=?value}
}

blog ?對象可以這樣來修改自身:

blog.modifySelf('author',?'Tapas');

調解(Intercession)

元編程中的 ?調解 ?指的是改變其它對象的語義。在 ES6 之前,可以用 ?Object.defineProperty() ?方法來改變對象的語義:

var?sun?=?{};
Object.defineProperty(sun,?'rises',?{value:?true,configurable:?false,writable:?false,enumerable:?false
});console.log('sun?rises',?sun.rises);
sun.rises?=?false;
console.log('sun?rises',?sun.rises);

輸出:

sun?rises?true
sun?rises?true

如你所見,我們創建了一個普通對象 ?sun,之后改變了它的語義:為其定義了一個不可寫的 ?rises ?屬性。

現在,我們深入了解一下 ?Reflect ?和 ?Proxy ?對象以及它們的用法。

Reflect API

在 ES6 中,Reflect 是一個新的 ?全局對象(像 math 一樣),它提供了一些工具函數,其中一些函數與 ?Object ?或 ?Function ?對象中的同名方法功能是相同的。

這些都是自省方法,可以用它們在運行時獲取程序內部信息。

以下是 ?Reflect ?對象提供的方法列表。點擊此處可以查看這些方法的詳細信息。

//?Reflect?對象方法Reflect.apply()
Reflect.construct()
Reflect.get()
Reflect.has()
Reflect.ownKeys()
Reflect.set()
Reflect.setPrototypeOf()
Reflect.defineProperty()
Reflect.deleteProperty()
Reflect.getOwnPropertyDescriptor()
Reflect.getPrototypeOf()
Reflect.isExtensible()

等等,現在問題來了:既然 ?Object ?或 ?Function ?對象中已經有這些方法了,為什么還要引入新的 API 呢?

困惑嗎?讓我們一探究竟。

集中在一個命名空間

JavaScript 已經支持對象反射,但是這些 API 沒有集中到一個命名空間中。從 ES6 開始,它們被集中到 ?Reflect ?對象中。

與其他全局對象不同,Reflect 不是一個構造函數,不能使用 new 操作符來調用它,也不能將它當做函數來調用。Reflect ?對象中的方法和 math 對象中的方法一樣是 ?靜態 ?的。

易于使用

Object ?對象中的 ?自省 ?方法在操作失敗的時候會拋出異常,這給開發者增加了處理異常的負擔。

也許你更傾向于把操作結果當做布爾值來處理,而不是去處理異常,借助 Reflect 對象就可以做到。

以下是使用 Object.defineProperty 方法的示例:

?try?{Object.defineProperty(obj,?name,?desc);//?執行成功}?catch?(e)?{//?執行失敗,處理異常}

使用 Reflect API 的方式如下:

if?(Reflect.defineProperty(obj,?name,?desc))?{//?執行成功
}?else?{//?處理執行失敗的情況。(這種處理方式好多了)
}

一等函數的魅力

我們可以通過 ?(prop in obj) ?操作來判斷對象中是否存在某個屬性。如果多次用到這個操作,我們需要把它封裝成函數。

在 ES6 的 ?Reflect API ?中已經包含了這些方法,例如,Reflect.has(obj, prop) 和 (prop in obj) 功能是一樣的。

看看另一個刪除對象屬性的示例:

const?obj?=?{?bar:?true,?baz:?false};//?We?define?this?function
function?deleteProperty(object,?key)?{delete?object[key];
}
deleteProperty(obj,?'bar');

使用 Reflect API 的方式如下:

//?使用?Reflect?API
Reflect.deleteProperty(obj,?'bar');

以更可靠的方式來使用 apply() 方法

在 ES5 中,我們可以使用 ?apply() ?方法來調用一個函數,并指定 ?this ?上下文、傳入一個參數數組。

Function.prototype.apply.call(func,?obj,?arr);
//?or
func.apply(obj,?arr);

這種方式比較不可靠,因為 ?func ?可能是一個具有自定義 ?apply ?方法的對象。

ES6 提供了一個更加可靠、優雅的方式來解決這個問題:

Reflect.apply(func,?obj,?arr);

這樣,如果 ?func ?不是可調用對象,會拋出 ?TypeError。此外 ?Reflect.apply() ?也更簡潔、易于理解。

幫助實現其他類型的反射

等我們了解 ?Proxy ?對象之后就能理解這句話意味著什么。在許多場景中,Reflect API 方法可以和 Proxy 結合使用。

Proxy 對象

ES6 的 ?Proxy ?對象可以用于 ?調解(intercession)

proxy ?對象允許我們自定義一些基本操作的行為(例如屬性查找、賦值、枚舉、函數調用等)。

以下是一些有用的術語:

  • target:代理為其提供自定義行為的對象。

  • handler:包含“捕獲器”方法的對象。

  • trap:“捕獲器”方法,提供了訪問目標對象屬性的途徑,這是通過 Reflect API 中的方法實現的。每個“捕獲器”方法都對應著 Reflect API 中的一個方法。

它們的關系如圖所示:

先定義一個包含“捕獲器”函數的 handler 對象,再使用這個 handler 和目標對象來創建一個代理對象,這個代理對象會應用 handler 中的自定義行為。

如果你對上面介紹的內容不太理解也沒關系,我們可以通過代碼示例來掌握它。

以下是創建代理對象的語法:

let?proxy?=?new?Proxy(target,?handler);

有許多捕獲器(handler 方法)可以用來訪問或者自定義目標對象。以下是捕獲器方法列表,可以在此處查看更多詳細介紹。

handler.apply()
handler.construct()
handler.get()
handler.has()
handler.ownKeys()
handler.set()
handler.setPrototypeOf()
handler.getPrototypeOf()
handler.defineProperty()
handler.deleteProperty()
handler.getOwnPropertyDescriptor()
handler.preventExtensions()
handler.isExtensible()

注意每個捕獲器都對應著 ?Reflect ?對象的方法,也就是說可以在許多場景下同時使用 ?Reflect ?和 ?Proxy

如何獲取不可用的對象屬性值

以下是一個打印 ?employee ?對象的屬性的示例:

const?employee?=?{firstName:?'Tapas',lastName:?'Adhikary'
};console.log(employee.firstName);
console.log(employee.lastName);
console.log(employee.org);
console.log(employee.fullName);

預期輸出:

Tapas
Adhikary
undefined
undefined

使用 Proxy 對象為 ?employee ?對象增加一些自定義行為。

步驟 1:創建一個使用 get 捕獲器的 Handler

我們使用名為 ?get ?的捕獲器,可以通過它來獲取對象的屬性值。handler 代碼如下:

let?handler?=?{get:?function(target,?fieldName)?{????????if(fieldName?===?'fullName'?)?{return?`${target.firstName}?${target.lastName}`;}return?fieldName?in?target??target[fieldName]?:`No?such?property?as,?'${fieldName}'!`}
};

以上 handler 代碼創建了 ?fullName ?屬性的值,還為訪問的屬性不存在的情況提供了更優雅的錯誤提示。

步驟 2:創建 Proxy 對象

目標對象 ?employee ?和 handler 都準備好了,可以這樣來創建 Proxy 對象:

let?proxy?=?new?Proxy(employee,?handler);

步驟 3:訪問 Proxy 對象的屬性

現在可以通過 proxy 對象來訪問 employee 對象的屬性,如下所示:

console.log(proxy.firstName);
console.log(proxy.lastName);
console.log(proxy.org);
console.log(proxy.fullName);

預期輸出:

Tapas
Adhikary
No?such?property?as,?'org'!
Tapas?Adhikary

注意我們是如何神奇地改變 ?employee ?對象的。

使用 Proxy 來驗證屬性值

創建一個 proxy 對象來驗證整數值。

步驟 1:創建一個使用 set 捕獲器的 handler

handler 代碼如下:

const?validator?=?{set:?function(obj,?prop,?value)?{if?(prop?===?'age')?{if(!Number.isInteger(value))?{throw?new?TypeError('Age?is?always?an?Integer,?Please?Correct?it!');}if(value?<?0)?{throw?new?TypeError('This?is?insane,?a?negative?age?');}}}
};

步驟 2:創建一個 Proxy 對象

代碼如下:

let?proxy?=?new?Proxy(employee,?validator);

步驟 3:將一個非整數值賦值給 age 屬性

代碼如下:

proxy.age?=?'I?am?testing?a?blunder';?//?string?value

預期輸出:

TypeError:?Age?is?always?an?Integer,?Please?Correct?it!at?Object.set?(E:\Projects\KOSS\metaprogramming\js-mtprog\proxy\userSetProxy.js:28:23)at?Object.<anonymous>?(E:\Projects\KOSS\metaprogramming\js-mtprog\proxy\userSetProxy.js:40:7)at?Module._compile?(module.js:652:30)at?Object.Module._extensions..js?(module.js:663:10)at?Module.load?(module.js:565:32)at?tryModuleLoad?(module.js:505:12)at?Function.Module._load?(module.js:497:3)at?Function.Module.runMain?(module.js:693:10)at?startup?(bootstrap_node.js:188:16)at?bootstrap_node.js:609:3

再試試以下操作:

p.age?=?-1;?//?拋出?TypeError

如何同時使用 Proxy 和 Reflect

下面是一個在 handler 中使用 Reflect API 方法的示例:

const?employee?=?{firstName:?'Tapas',lastName:?'Adhikary'
};let?logHandler?=?{get:?function(target,?fieldName)?{????????console.log("Log:?",?target[fieldName]);//?Use?the?get?method?of?the?Reflect?objectreturn?Reflect.get(target,?fieldName);}
};let?func?=?()?=>?{let?p?=?new?Proxy(employee,?logHandler);p.firstName;p.lastName;
};func();

其它使用場景

還有許多場景可以用到 Proxy 概念:

  • 保護對象的 ?ID ?字段不被刪除(deleteProperty 捕獲器)

  • 追蹤屬性訪問的過程(get、set 捕獲器)

  • 數據綁定(set 捕獲器)

  • 可撤銷的引用

  • 控制 ?in ?操作符的行為

......以及更多

元編程陷阱

盡管 ?元編程 ?概念為我們提供了強大的功能,但是使用不當也會引發錯誤。

要當心強大功能的副作用

注意:

  • 功能過于強大,務必理解了之后再使用。

  • 可能會影響性能。

  • 可能會使代碼難以調試。

總結

總而言之:

  • Reflect ?和 ?Proxy ?是 JavaScript 的優秀特性,有助于實現元編程。

  • 利用它們可以解決許多復雜的問題。

  • 同時也要注意它們的弊端。

  • ES6 Symbols 也能用來改變現有的類或對象的行為。

希望本文對你有所幫助,文中所有源碼都可以在我的 GitHub 倉庫?中查看。

歡迎分享本文。歡迎關注我的 Twitter 賬號(@tapasadhikary)并留言討論。


原文鏈接:https://www.freecodecamp.org/news/what-is-metaprogramming-in-javascript-in-english-please/

作者:TAPAS ADHIKARY

譯者:Humilitas

在線技術翻譯工作坊預告

在線技術翻譯工作坊將于北京時間 1 月 9 日?周六下午 13:00 - 15:00 開展(每周都在這個時間開展)。

歡迎大家添加小助手微信 fcczhongguo,加入會議室。

非營利組織 freeCodeCamp.org 自 2014 年成立以來,以“幫助人們免費學習編程”為使命,創建了大量免費的編程教程,包括交互式課程、視頻課程、文章等。我們正在幫助全球數百萬人學習編程,希望讓世界上每個人都有機會獲得免費的優質的編程教育資源,成為開發者或者運用編程去解決問題。

你也想成為

freeCodeCamp 社區的貢獻者嗎

歡迎點擊以下文章了解

??

freeCodeCamp 在線翻譯工作坊丨學編程,練英語,成為開源貢獻者

成為 freeCodeCamp 專欄作者,與世界各地的開發者分享技術知識

點擊“閱讀原文”

在?freeCodeCamp 專欄

推薦閱讀

若川知乎高贊:有哪些必看的 JS庫?
我在阿里招前端,我該怎么幫你?(現在還可以加模擬面試群)
如何拿下阿里巴巴 P6 的前端 Offer
如何準備阿里P6/P7前端面試--項目經歷準備篇
大廠面試官常問的亮點,該如何做出?
如何從初級到專家(P4-P7)打破成長瓶頸和有效突破
若川知乎問答:2年前端經驗,做的項目沒什么技術含量,怎么辦?

末尾

你好,我是若川,江湖人稱菜如若川,歷時一年只寫了一個學習源碼整體架構系列~(點擊藍字了解我)

  1. 關注若川視野,回復"pdf" 領取優質前端書籍pdf,回復"1",可加群長期交流學習

  2. 我的博客地址:https://lxchuan12.gitee.io?歡迎收藏

  3. 覺得文章不錯,可以點個在看呀^_^另外歡迎留言交流~

精選前端好文,伴你不斷成長

若川原創文章精選!可點擊

小提醒:若川視野公眾號面試、源碼等文章合集在菜單欄中間【源碼精選】按鈕,歡迎點擊閱讀,也可以星標我的公眾號,便于查找

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276237.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276237.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276237.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python 異常處理模塊_擴展Python模塊系列(五)----異常和錯誤處理

在上一節中&#xff0c;討論了在用C語言擴展Python模塊時&#xff0c;應該如何處理無處不在的引用計數問題。重點關注的是在實現一個C Python的函數時&#xff0c;對于一個PyObject對象&#xff0c;何時調用Py_INCREF和Py_DECREF。在編寫C語言代碼時&#xff0c;需要了解Python…

常見的php筆試題(附答案)搜集整理

轉載鏈接&#xff1a;http://www.yaojinbu.com/p/139.html 常見的php筆試題&#xff08;附答案&#xff09;搜集整理 1.在PHP中&#xff0c;當前腳本的名稱&#xff08;不包括路徑和查詢字符串&#xff09;記錄在哪個預定義變量中&#xff1f;而鏈接到當前頁面的URL又記錄在哪個…

Js整理備忘(02)——運算符

1、運算符的表示以及優先級 Javascript&#xff08;以下簡寫為Js&#xff09;的大部分運算符與C或Java是類似的。 記得剛學C語言時老師講的優先級口訣&#xff0c;非常好記&#xff1a;“括、單、算、移、關”“位、邏、條、賦、逗”&#xff0c;此處也可以套用一下&#xff0c…

手寫一個合格的前端腳手架

為什么我們需要一套腳手架為什么我們需要一套腳手架&#xff0c;它能幫助我們解決哪些痛點問題。?前端項目配置越來越繁瑣、耗時&#xff0c;重復無意義的工作?項目結構不統一、不規范?前端項目類型繁多&#xff0c;不同項目不同配置&#xff0c;管理成本高?腳手架也可以是…

第一篇cnblog!

本人才疏學淺&#xff0c;終于通過了cnblog的審核&#xff0c;興奮之余&#xff0c;發表感言——不容易啊&#xff01;在我的博聞里面&#xff0c;隨筆類當然就是技術類的比較多的&#xff0c;特別是實例類的。理論類的大部分放在文章板塊&#xff0c;本人e文特別好(哈哈&#…

解決error 1045: Access denied for user: 'root@localhost' (Using password: YES)

轉載連接&#xff1a;http://jianfw2009.blog.163.com/blog/static/13431366020111016112459158/ 1、先停止mysql服務2、在mysql的目錄下找到my.ini&#xff0c;在[mysqld]后面加上skip-grant-tables3、啟動mysql服務,打開Command Line Client以空密碼登錄4、退出mysql,并停止服…

fillcolor是什么意思_fill是什么意思

1. (使)裝滿;(使)注滿;(使)充滿If you fill a container or area, or if it fills, an amount of something enters it that is enough to make it full.e.g.Fill a saucepan with water and bring to a slow boil...往平底鍋里加滿水,小火煮沸。e.g.She made sandwiches, fill…

利用JMeter進行壓力測試(1)(轉)

轉自&#xff1a;http://www.cnblogs.com/game-over/archive/2010/01/08/1642685.html壓力測試以軟件響應速度為測試目標&#xff0c;尤其是在較短時間內大量并發用戶的同時訪問時&#xff0c;軟件的性能和抗壓能力。 JMeter是一款開源的壓力測試工具&#xff0c;目前最新Rele…

MyISAM InnoDB 區別

轉載鏈接&#xff1a;http://www.php100.com/html/webkaifa/database/Mysql/2011/0326/7789.html MyISAM 和 InnoDB 講解 InnoDB和MyISAM是許多人在使用MySQL時最常用的兩個表類型&#xff0c;這兩個表類型各有優劣&#xff0c;視具體應用而定。基本的差別為&#xff1a;MyISAM…

Git 內部原理圖解——對象、分支以及如何從零開始建倉庫

我們中的許多人每天都在使用 git&#xff0c;但是有多少人知道它的內部是怎么運作的呢&#xff1f;例如我們使用 git commit 時發生了什么&#xff1f;提交&#xff08;commit&#xff09;與提交之間保存的是什么&#xff1f;兩次提交之間難道只是文件的差異&#xff08;dif…

wpsmac和pc版的區別_Mac版WPS Office和微軟Office 2019哪個更好?

眾所周知&#xff0c; macOS系統生態下&#xff0c;有許多界面精美、功能強大、體驗出色的軟件&#xff0c;但提到辦公套件&#xff0c;人們首先想到的還是微軟 Office 套件&#xff0c;其中的Word、 Excel 以及PPT&#xff0c;但用戶體驗并不如意。但現在&#xff0c;蘋果用戶…

A tutorial video for MindManager for free

MindManager 2016 for Windows Essential Training 本人學習的時候使用的是MindManager 2018版本的&#xff0c;和2016版本差異不大。 轉載于:https://www.cnblogs.com/kelamoyujuzhen/p/10253278.html

Google, 請不要離開我們!

雖然我是.net陣營, 力挺Silverlight, 但是我真心希望谷歌留在中國, 如果她能夠靠談判求的言論自由的權利, 那將對中國的擁有自由信仰的一族產生重大的影響. 谷歌離開了中國, 不是她想拋棄中國市場, 而是中國決策者背叛了人性. 在此留下 Google 2010年1月14日的logo, 智慧的幽默…

高級php面試題及部分答案

轉載鏈接&#xff1a;http://www.2cto.com/kf/201304/201112.html 一. 基本知識點 1.1 HTTP協議中幾個狀態碼的含義:503 500 401 403 404 200 301 302。。。 200 : 請求成功&#xff0c;請求的數據隨之返回。 301 : 永久性重定向。 302 : 暫時行重定向。 401 : 當前請求需要用…

iec104點號_IEC104報文流程(有常用類型標識解釋)

參數地址圍類別97版基地址2002版基地址遙信1H------400H1H------4000H遙測701H------900H4001H------5000H遙控B01------B806001H------6100H設點B81H------C00H6201H------6400H電度C01H------C80H6401H------6600H遙測和遙信個數不設置上限&#xff0c;可以沒有上限限制&…

本周ASP.NET英文技術文章推薦[04/08 - 04/14](附贈自彈超級瑪麗主題曲)

摘要 本期共有6篇文章&#xff1a; ASP.NET編譯問題的公開Hotfix補丁 期待下個版本AjaxPro 的發布 在ASP.NET 2.0中使用MultiView控件實現多頁面表單 數據綁定的技巧&#xff1a;嵌套Eval語句 在ASP.NET 2.0中訪問并更新數據&#xff1a;使用數據源控件以編程方式訪問數據 AD…

一個離開某門戶網站人員自爆黑幕

去年&#xff0c;我已在星星發表了一個關于免費發短信的各類軟件的黑幕所在。而事實上的SMS&#xff08;即短信&#xff09;的黑幕遠不止于此&#xff0c;今天&#xff0c;我終于有空坐下來&#xff0c;把其中的一些讓你感覺平常卻實際觸目驚心的事情告訴你們&#xff0c;讓你們…

28歲自學3年前端成功轉行的勵志故事

為什么轉行因為混得不好。在成為程序員之前&#xff0c;我干過很多工作。由于學歷的問題&#xff08;高中&#xff09;&#xff0c;我的工作基本上都是體力活。包括但不限于&#xff1a;工廠普工、銷售&#xff08;沒有干銷售的才能&#xff09;、搬運工、擺地攤等&#xff0c;…

css中!important的作用

轉載鏈接&#xff1a;http://www.cnblogs.com/guoguo-15/archive/2011/08/24/2151859.html css中!important的作用 {*rule !important}這個css規則當今在網頁制作的時候的普及已經非常流行了&#xff0c;以前我對它的理解就停留在‘瀏覽器是否識別階段’ 而沒有真正去研究過&am…

word2vec應用場景_word2vec的使用參數解釋和應用場景

" > corpus.txt因為這些數據雖然去除了其他標簽的數據&#xff0c;但是卻把保留下來了&#xff0c;所以后來作者在分詞程序中去除了這個標簽我在這個[網頁](http://www.jb51.net/article/65497.htm)上找到了一個python去標簽的簡單代碼。但是沒有實驗過&#xff0c;不知…