<dfn id="hx5t3"><strike id="hx5t3"><em id="hx5t3"></em></strike></dfn>

    <thead id="hx5t3"></thead><nobr id="hx5t3"><font id="hx5t3"><rp id="hx5t3"></rp></font></nobr>

    <listing id="hx5t3"></listing>

    <var id="hx5t3"></var>
    <big id="hx5t3"></big>

      
      

      <output id="hx5t3"><ruby id="hx5t3"></ruby></output>
      <menuitem id="hx5t3"><dfn id="hx5t3"></dfn></menuitem>

      <big id="hx5t3"></big>

        陳其文

        陳其文 查看完整檔案

        成都編輯  |  填寫畢業院校  |  填寫所在公司/組織填寫個人主網站
        編輯

        前端

        個人動態

        陳其文 贊了文章 · 2月1日

        【AST篇】手把手教你寫Eslint plugin

        3465B16F-CD89-4B89-A97F-BEB049661C19.png

        前言

        雖然現在已經有很多實用的 ESLint 插件了,但隨著項目不斷迭代發展,你可能會遇到已有 ESLint 插件不能滿足現在團隊開發的情況。這時候,你需要自己來創建一個 ESLint 插件。

        本文我將帶你了解各種Lint工具的大致歷史,然后一步一步地創建一個屬于你自己的 ESLint 插件,以及教你如何利用AST抽象語法樹來制定這個插件的規則。

        以此來帶你了解 ESLint 的實現原理。

        目標&涉及知識點

        本文 ESLint 插件目標是在項目開發中禁用:console.time()。

        • [x] AST 抽象語法樹
        • [x] ESLint
        • [x] Npm 發布
        • [x] 單元測試

        插件腳手架構建

        這里我們利用 yeomangenerator-eslint 來構建插件的腳手架代碼。安裝:

        npm install -g yo generator-eslint

        本地新建文件夾eslint-plugin-demofortutorial:

        mkdir eslint-plugin-demofortutorial
        cd eslint-plugin-demofortutorial

        初始化 ESLint 插件的項目結構:

        yo eslint:plugin // 搭建一個初始化的目錄結構

        此時文件的目錄結構為:

        .
        ├── README.md
        ├── lib
        │?? ├── index.js
        │?? └── rules
        ├── package.json
        └── tests
            └── lib
                └── rules

        安裝依賴:

        npm install

        至此,環境搭建完畢。

        創建規則

        終端執行:

        yo eslint:rule // 生成默認 eslint rule 模版文件


        此時項目結構為:

        .
        ├── README.md
        ├── docs // 使用文檔
        │?? └── rules
        │??     └── no-console-time.md
        ├── lib // eslint 規則開發
        │?? ├── index.js
        │?? └── rules // 此目錄下可以構建多個規則,本文只拿一個規則來講解
        │??     └── no-console-time.js
        ├── package.json
        └── tests // 單元測試
            └── lib
                └── rules
                    └── no-console-time.js

        上面結構中,我們需要在 ./lib/ 目錄下去開發 Eslint 插件,這里是定義它的規則的位置。

        AST 在 ESLint 中的運用

        在正式寫 ESLint 插件前,你需要了解下 ESLint 的工作原理。其中 ESLint 使用方法大家應該都比較熟悉,這里不做講解,不了解的可以點擊官方文檔如何在項目中配置 ESLint。

        在公司團隊項目開發中,不同開發者書寫的源碼是各不相同的,那么在 ESLint 中,如何去分析每個人寫的源碼呢?

        作為開發者,面對這類問題,我們必須懂得要使用 抽象的手段 !那么 Javascript 的抽象性如何體現呢?

        沒錯,就是 AST
        (Abstract Syntax Tree(抽象語法樹)),再祭上那張看了幾百遍的圖。

        ESLint 中,默認使用 esprima 來解析我們書寫的 Javascript 語句,讓其生成抽象語法樹,然后去 攔截 檢測是否符合我們規定的書寫方式,最后讓其展示報錯、警告或正常通過。 ESLint 的核心就是規則(rules),而定義規則的核心就是利用 AST 來做校驗。每條規則相互獨立,可以設置禁用off、警告warn??和報錯error?,當然還有正常通過不用給任何提示。

        規則創建

        上面講完了 ESLintAST 的關系之后,我們可以正式進入開發具體規則。先來看之前生成的 lib/rules/no-console-time.js:

        /**
         * @fileoverview no console.time()
         * @author Allan91
         */
        "use strict";
        
        //------------------------------------------------------------------------------
        // Rule Definition
        //------------------------------------------------------------------------------
        
        module.exports = {
            meta: {
                docs: {
                    description: "no console.time()",
                    category: "Fill me in",
                    recommended: false
                },
                fixable: null,  // or "code" or "whitespace"
                schema: [
                    // fill in your schema
                ]
            },
        
            create: function(context) {
        
                // variables should be defined here
        
                //----------------------------------------------------------------------
                // Helpers
                //----------------------------------------------------------------------
        
                // any helper functions should go here or else delete this section
        
                //----------------------------------------------------------------------
                // Public
                //----------------------------------------------------------------------
        
                return {
        
                    // give me methods
        
                };
            }
        };

        這個文件給出了書寫規則的模版,一個規則對應一個可導出的 node 模塊,它由 metacreate 兩部分組成。

        • meta 代表了這條規則的元數據,如其類別,文檔,可接收的參數的 schema 等等。
        • create:如果說 meta 表達了我們想做什么,那么 create 則用表達了這條 rule 具體會怎么分析代碼;

        Create 返回一個對象,其中最常見的鍵名AST抽象語法樹中的選擇器,在該選擇器中,我們可以獲取對應選中的內容,隨后我們可以針對選中的內容作一定的判斷,看是否滿足我們的規則。如果不滿足,可用 context.report 拋出問題,ESLint 會利用我們的配置對拋出的內容做不同的展示。

        具體參數配置詳情見官方文檔

        本文創建的 ESLint 插件是為了不讓開發者在項目中使用 console.time(),先看看這段代碼在抽象語法樹中的展現:

        其中,我們將會利用以下內容作為判斷代碼中是否含有 console.time:

        那么我們根據上面的AST(抽象語法書)在 lib/rules/no-console-time.js 中這樣書寫規則:

        /**
         * @fileoverview no console.time()
         * @author Allan91
         */
        "use strict";
        
        //------------------------------------------------------------------------------
        // Rule Definition
        //------------------------------------------------------------------------------
        
        module.exports = {
            meta: {
                docs: {
                    description: "no console.time()",
                    category: "Fill me in",
                    recommended: false
                },
                fixable: null,  // or "code" or "whitespace"
                schema: [
                    // fill in your schema
                ],
                // 報錯信息描述
                messages: {
                    avoidMethod: "console method '{{name}}' is forbidden.",
                },
            },
        
            create: function(context) {
                return {
                    // 鍵名為ast中選擇器名
                    'CallExpression MemberExpression': (node) => {
                        // 如果在ast中滿足以下條件,就用 context.report() 進行對外警告??
                        if (node.property.name === 'time' && node.object.name === 'console') {
                            context.report({
                                node,
                                messageId: 'avoidMethod',
                                data: {
                                    name: 'time',
                                },
                            });
                        }
                    },
                };
            }
        };
        

        再修改 lib/index.js

        "use strict";
        
        module.exports = {
            rules: {
                'no-console-time': require('./rules/no-console-time'),
            },
            configs: {
                recommended: {
                    rules: {
                        'demofortutorial/no-console-time': 2, // 可以省略 eslint-plugin 前綴
                    },
                },
            },
        };

        至此,Eslint 插件創建完成。接下去你需要做的就是將此項目發布到 npm平臺。
        根目錄執行:

        npm publish

        打開npm平臺,可以搜索到上面發布的 eslint-plugin-demofortutorial 這個 Node 包。

        如何使用

        發布完之后在你需要的項目中安裝這個包:

        npm install eslint-plugin-demofortutorial -D

        然后在 .eslintrc.js 中配置:

        "extends": [
            "eslint:recommended",
            "plugin:eslint-plugin-demofortutorial/recommended",
        ],
        "plugins": [
            'demofortutorial'
        ],

        如果之前沒有.eslintrc.js 文件,可以執行下面命令生成:

        npm install -g eslint
        eslint --init

        此時,如果在當前項目的 JS 文件中書寫 console.time,會出現如下效果:

        單元測試(完善)

        對于完整的 npm 包來說,上面還只算是個“半成品”,我們需要寫單元測試來保證它的完整性和安全性。

        下面來完成單元測試,在 ./tests/lib/rules/no-console-time.js 中編寫如下代碼:

        'use strict';
        
        // ------------------------------------------------------------------------------
        // Requirements
        // ------------------------------------------------------------------------------
        
        let rule = require('../../../lib/rules/no-console-time');
        
        let RuleTester = require('eslint').RuleTester;
        
        // ------------------------------------------------------------------------------
        // Tests
        // ------------------------------------------------------------------------------
        
        let ruleTester = new RuleTester({
            parserOptions: {
                ecmaVersion: 10,
            },
        });
        
        ruleTester.run('no-console-time', rule, {
        
            valid: [ // 合法示例
                '_.time({a:1});',
                "_.time('abc');",
                "_.time(['a', 'b', 'c']);",
                "lodash.time('abc');",
                'lodash.time({a:1});',
                'abc.time',
                "lodash.time(['a', 'b', 'c']);",
            ],
        
            invalid: [ // 不合法示例
                {
                    code: 'console.time()',
                    errors: [
                        {
                            messageId: 'avoidMethod',
                        },
                    ],
                },
                {
                    code: "console.time.call({}, 'hello')",
                    errors: [
                        {
                            messageId: 'avoidMethod',
                        },
                    ],
                },
                {
                    code: "console.time.apply({}, ['hello'])",
                    errors: [
                        {
                            messageId: 'avoidMethod',
                        },
                    ],
                },
                {
                    code: 'console.time.call(new Int32Array([1, 2, 3, 4, 5]));',
                    errors: 1,
                },
            ],
        });
        

        上面測試代碼詳細介紹見官方文檔。

        根目錄執行:

        npm run test

        至此,這個包的開發完成。其它規則開發也是類似,比如您可以繼續制定其它規范,比如 ?console.log()
        、debugger警告等等。

        其它

        由于自動生成ESLint的項目中依賴的 eslint 版本還在 3.x 階段,會對單元測試語法解析造成如下報錯:

        'Parsing error: Invalid ecmaVersion.'

        建議將該包升級到 "eslint": "^5.16.0" 。

        以上。

        查看Github上的項目倉庫

        查看Npm上發布的包

        查看原文

        贊 11 收藏 4 評論 0

        陳其文 發布了文章 · 1月29日

        eslint-plugin初體驗——定時器檢測

        前言

        很久沒有寫文章了,主要是沒什么值得寫的。
        打工人天天搬磚,確實寫無可寫。
        直到最近整理團隊規范,總算遇到了值得一寫的內容。

        遇見

        整理代碼的時候,在項目中發現了很多定時器,比如:

        setInterval(() => {
            this.getData()
        }, 1e4)

        這是定時輪詢,更新數據的一段代碼。翻了翻歷史記錄,是個新來的實習生寫的。
        因為沒有把定時器存儲起來,自然不可能取消。
        所以每次打開這個組件,就會產生一個定時器,如果用戶多次進入這個頁面,那就很糟糕了。

        解決

        全局查找setInterval一個個處理,其實問題也不大。我找了下,大概也就百十來個setInterval!當然不會是每個都需要解決,但是逐個查看解決,也有點燥人。
        所以最后的解決方案是,通過eslint查找,然后統一解決。最主要的是,避免后面的代碼繼續出現這種情況。

        嘗試

        網上有很多現成的demo,但是信息大多比較散亂。比如有的demo版本比較低,雖然可以運行,項目引用就不行。
        有的demo不完整,還需要自己補全。
        當然,最主要的還是,我壓根沒了解過eslint的運行規則……
        不得已,查資料,debugger,一步步前進。

        AST抽象語法樹

        eslint怎么運行?這就不得不接觸到AST的概念。
        代碼拆解,從語句,語句塊,表達式,函數,聲明,等等概念,全部拆解到樹形結構上,一一映射。
        在這篇文章上已經講得比較細致:AST抽象語法樹.

        eslint-plugin

        eslint-plugin有自己的規范。
        我們要開發一個插件,就必須遵循規范。
        恰恰是在這個規范上卡了我比較長時間——整整一天。
        我找了相關的demo,自己寫了一個例子,查找沒有存儲的定時器。很快例子就寫好了,也通過測試。
        于是我就開開心心的發到npm,然后在項目中引用,運行代碼,一切都很順利。
        但是什么提示都沒有?。?!
        黑人臉?。?!

        debugger

        在node_modules中調試代碼,繼續運行。
        但是更加奇怪,連我打的debugger都沒執行。
        不斷修改,再運行,還是沒有。
        如此數次下來,漸漸懷疑人生。
        難道是.eslintrc.js配置不對?
        天知道,我壓根沒讀過.eslintrc.js的配置,我只是一個搬運工。
        好吧,先把.eslintrc.js讀一遍。
        原來,我真的沒有配對.eslintrc.js。。。
        一番折騰之后,終于有了提示 definition for rule 'no-record-time' was not found。
        好吧,no-record-time是我引入的npm包,繼續折騰。
        因為開發方式的原因,我需要用以下方式引入:

          plugins: [
            "no-record-time",
          ],
          rules: {
            'no-record-time/no-record-time': 2
          },

        解決了這步,信心大增。

        然后,我又發現了一個命令npm run lint --debug,這個命令可以直接把eslint運行的結果打印出來,我終于不用改一句代碼就重啟一次編輯器,如虎添翼。
        但是,結果很奇怪,我寫的規則突然不生效了。
        怎么肥事?

        Parser API

        開始我用的是Program:exit查找,這是Parser API提供的一個對象,eslint的規范可以返回這個屬性,執行所有Program節點的代碼遍歷。
        在測試中,我寫的規則可以被很好的運行。但是到了項目中,就不行了。
        原因未知。
        但是我發現了一個更好的對象——ExpressionStatement。
        這是一個表達式語句對象。
        如上文:

        setInterval(() => {
            this.getData()
        }, 1e4)

        這就是一個表達式語句。
        如果這樣就不是表達式了:

        let timer = setInterval(() => {
            this.getData()
        }, 1e4)

        這是一個聲明賦值語句。
        所以可以直接查找表達式語句對象。
        一通操作,把節點對象打印之后,發現了規律。
        node.expression.callee.name這個屬性可以讀取表達式的函數名,當然,前提是必須有node.expression.callee。
        那么判斷條件就很容易了:

        function getSetInterval(node) {
          if (node.expression.callee && node.expression.callee.name === 'setInterval') {
            return false
          }
          if (node.expression.callee && node.expression.callee.name === 'setTimeout') {
            return false
          }
          return true
        }

        這樣就可以找到沒存儲的定時器了。

        完整代碼

        /**
         * @fileoverview Rule to flag timer didn't record.
         * @author 陳其文<jianwang19@sina.com>
         * @copyright 2021-01 All rights reserved.
         */
        "use strict";
        
        module.exports = {
          meta: {
            type: "problem",
        
            docs: {
              description: "定時器檢測",
              category: "ExpressionStatement",
              recommended: true,
            },
        
            schema: []
          },
          create: function (context) {
            function getSetInterval(node) {
              if (node.expression.callee && node.expression.callee.name === 'setInterval') {
                return false
              }
              if (node.expression.callee && node.expression.callee.name === 'setTimeout') {
                return false
              }
              return true
            }
            return {
              ExpressionStatement(node){
                let state = getSetInterval(node)
                if (!state) {
                  context.report(node, '{{ name }} timer didn\'t record', {
                    name: node.expression.callee.name
                  });
                }
              },
            };
          },
        };

        最后發現,其實很簡單。
        還沒經過完整測試,也許存在bug。
        但終究是邁出了一大步。
        git倉庫如下:eslint-plugin-no-record-time

        查看原文

        贊 0 收藏 0 評論 0

        陳其文 發布了文章 · 2020-09-25

        兩份json數據比較

        前言

        有一個工具。
        可以在線編輯geojson數據。
        主要功能是,在線編輯建筑,增刪改,主要數據是面積和高度。面積由軌跡數組組成。
        軌跡數組:features.geometry.coordinates。
        高度是:features.properties.Height。
        編輯了可以導出成一份geojson文件,也可導出成obj文件。
        今天遇到的問題是,如果我想知道我編輯了多少個建筑,怎么辦?

        經過

        原則上可以在編輯的過程中記錄,但這樣做不是很好。

        • 已經改了的文件,沒辦法重新處理
        • 增加工作量
        • 增加邏輯處理,影響性能

        過程

        • 最簡單的方法是,直接使用for循環,遍歷兩份數據,如果能夠找到相同的數據,即是未修改,否則修改+1.
        • 但是這樣做有兩個問題。
        • 因為精度問題,導入的原始數據和導出的數據,就算沒改變,也會有差異,因為小數點足足有14位。
        • 直接循環時間復雜度是O(n2)。

        怎么辦呢?
        經過測試,發現兩份數據在保留4位小數點的時候,是一致的。
        所以可以把數據取4個小數點。
        數據差異的問題已經解決了。
        但是時間復雜度還沒解決。
        于是決定用空間換時間。

        算法

        用一個對象,把每一個建筑的軌跡存儲到哈希之中,哈希的值為建筑的高度。
        如:
        features.properties.Height = 9
        features.geometry.coordinates = [[[114.35098683912278, 36.13595280429963],[114.35098693920013, 36.13582170962971]]]
        則存儲為:
        let obj = {}
        obj['114.3509-36.1359-114.3509-36.1358'] = 9
        因為不同建筑的高度、軌跡不同,所以同一份數據的哈希是唯一的。
        第一份數據存儲起來,跟第二份數據比較,記錄下不相同的數據,就可以找到改變的數據。

        備注

        為了避免哈希重復,在記錄哈希的時候需要做一個重復判斷。

        附錄

        import gai from './assets/gai.js'
        
         import yuan from './assets/yuan.js'
        
         let gaiobj =?{}
        
         let yuanobj =?{}
        
         for(let i = 0; i < yuan.features.length; i++){
        
         let features = yuan.features[i]
        
         let properties = features.properties
        
         let geometry = features.geometry
        
         let coordinates = geometry.coordinates[0]
        
         let lnglat = coordinates[0]
        
         let height = Number(properties.Height || properties.height)
        
         let key = ''
        
         lnglat.forEach(xy => {
        
         let [x, y]?= xy
        
         key += x.toFixed(4)?+ '-'+ y.toFixed(4)?+ '-'
        
         })
        
         if(yuanobj[key]){
        
         console.log('key1', key)
        
         }
        
         yuanobj[key]?= height
        
         }
        
         let change = 0
        
         for(let i = 0; i < gai.features.length; i++){
        
         let features = gai.features[i]
        
         let properties = features.properties
        
         let geometry = features.geometry
        
         let coordinates = geometry.coordinates[0]
        
         let lnglat = coordinates[0]
        
         let height = Number(properties.Height || properties.height)
        
         let key = ''
        
         lnglat.forEach(xy => {
        
         let [x, y]?= xy
        
         key += x.toFixed(4)?+ '-'+ y.toFixed(4)?+ '-'
        
         })
        
         if(gaiobj[key]){
        
         console.log('key2', key)
        
         }
        
         gaiobj[key]?= height
        
         if(yuanobj[key]?!= gaiobj[key]){
        
         change ++
        
         }
        
         }
        
         console.log('已經改變了的建筑:', change)
        查看原文

        贊 0 收藏 0 評論 0

        陳其文 發布了文章 · 2020-08-14

        chrome iframe cookies設置失敗

        前言

        新版Chrome增加了一個功能 SameSite,可以禁止iframe設置cookies。

        如果我們在iframe中需要登錄驗證,那就比較麻煩??梢酝ㄟ^禁用這個功能解決。

        例子

        瀏覽器控制臺提示:

        A cookie associated with a cross-site resource at http://58.211.78.91/ was set without the `SameSite` attribute. It has been blocked, as Chrome now only delivers cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/... and?https://www.chromestatus.com/...

        如果瀏覽器中提示以上內容,而且iframe中的請求中,出現以下情況:

        基本可以確定是_SameSite的問題。_

        解決

        Chrome訪問 chrome://flags/ ,搜索_SameSite,找到SameSite by default cookies,這是為Disabled,即可:_

        作用是,禁止瀏覽器的SameSite功能生效。

        查看原文

        贊 0 收藏 0 評論 0

        陳其文 發布了文章 · 2020-07-14

        class之super

        前言
        super是什么?
        也許很多人,如果沒有面向對象開發經驗,都不是很清楚。
        包括我。

        起因
        之前在項目中使用class,沒有用到繼承,都是直接聲明一個類,使用的時候,new一個實例,很簡單,但邏輯不是很清晰。
        今天有一個想法,把基礎功能抽離出來,做成基類,業務邏輯繼承基礎功能。這樣有兩個好處,一個就是基礎功能可以做成多個項目通用,另一個就是便于業務拆分。其實說到底,就是解耦。

        經過
        之前有多個功能,都寫在box類,項目入口就在box。
        如今從box抽出了一個基類lib,一個業務入口business,還有很多細分的業務模塊business-xx。
        項目入口改為business,business繼承lib,引入business-xx,實例化business-xx。
        代碼如下:

        import Rail from './rail'
        class Business extends Lib {
            constructor(){
                this.rail = new Rail(this)
            }
        }

        但是很遺憾,報錯:
        this hasn't been initialised - super() hasn't been called
        這是說我沒有執行super?
        搜了一下,原來這是調用父類的構造方法,加了一行代碼:

        super()

        然后還是報錯,不過這次不是語法報錯,是代碼賦值錯誤,因為在父類的構造方法中,需要參數。
        思考了一下,在super中傳遞了幾個參數,父類的構造函數控制臺打印,果然就是super中的參數。
        很顯然,super確實是調用父類的構造方法,而且可以傳遞參數。

        結果
        代碼如下:

        import Rail from './rail'
        class Business extends Lib {
            constructor(control){
                super(control)
                this.rail = new Rail(this)
            }
        }

        這樣,只要在項目的開始,實例化Business,就可以把對應的功能注冊進來,比如rail。
        而且后面如果需要添加功能,只需要如rail一樣,注冊一次就行。
        因為Business繼承自Lib,又把Business傳遞到了業務模塊內部,所以在業務模塊內部完全可以獲取到Lib的方法和屬性。
        control是整個大模塊Business和外部模塊通信的橋梁。

        結語
        曾經想過,每一個業務子模塊,比如rail,直接繼承自Lib,然后也是在Business中實例化。這樣會不會更合適呢?
        但是后來否決了這種想法,畢竟不同子業務模塊之間也存在通信的需求,如果做得太過獨立,通信也很麻煩。
        不如全部子模塊都掛載在Business中,通信方便。
        模塊化,也有一定的界限吧。

        查看原文

        贊 0 收藏 0 評論 0

        陳其文 發布了文章 · 2020-07-07

        不/常見HTTP狀態碼

        HTTP筆記
        101:websocket
        200:請求成功
        206:視頻文件
        304:本地緩存
        404:目錄找不到

        查看原文

        贊 0 收藏 0 評論 0

        陳其文 贊了回答 · 2020-04-28

        解決websocket客戶端發送的數據會按序到底服務器么?

        TCP 是保證順序的,分包有個序列號,丟包會重新傳。但是你說的兩條數據我不是很理解,如果是兩個請求,那當然是獨立的。

        關注 2 回答 1

        陳其文 提出了問題 · 2020-04-27

        解決websocket客戶端發送的數據會按序到底服務器么?

        瀏覽器連續發送十條消息給服務器,服務器接收到的時候是按順序的嗎?
        如果服務器連續下發十條消息,瀏覽器接收到的是按序嗎?

        對于TCP還不是很理解。
        TCP是分包發送數據的,但是如果有兩條數據,第一條很大,第二條很小,比如第一條數據A,分成100個數據包,第二條B分成1個數據包,一共101個數據包。
        在第100個的時候丟包了,這時候客戶端要求重發,但是第101數據包已經到達,數據B已經完整。
        這時候數據B會直接交給瀏覽器還是等數據A完整之后,先交數據A然后交數據B?

        關注 2 回答 1

        陳其文 贊了回答 · 2020-04-27

        解決class內部方法中this丟失

        瀏覽器全局對象this是window,而node中是global,嚴格模式下則是undefined。

        關注 2 回答 3

        陳其文 提出了問題 · 2020-04-24

        解決class內部方法中this丟失

        class A {
            a(){
                console.log(this)
                function b(){
                    console.log(this)
                }
                b()
            }
        }
        let a = new A()
        a.a()
        // A
        // undefined

        代碼如上,第一個this毫無疑問指向了當前的a,第二個this丟失了。
        想不明白為什么第二個this會丟失。

        關注 2 回答 3

        認證與成就

        • 獲得 68 次點贊
        • 獲得 22 枚徽章 獲得 1 枚金徽章, 獲得 3 枚銀徽章, 獲得 18 枚銅徽章

        擅長技能
        編輯

        開源項目 & 著作
        編輯

        注冊于 2018-06-22
        個人主頁被 4.3k 人瀏覽

        一本到在线是免费观看_亚洲2020天天堂在线观看_国产欧美亚洲精品第一页_最好看的2018中文字幕