沐鳴註冊平台官網_打造自適應網站只用一個 CSS 屬性就夠了

用一個css屬性創建一個響應式網站,讓我們來看看它是如何做到的。以這個模板為例,沒有應用css屬性。

使用 clamp() CSS函數,我們可以創建僅具有一個屬性的響應式網站。

現在添加魔術CSS

clamp(minimum, preferred, maximum);

在這裏!你已經完成了

說明

clamp() 的工作原理是“夾緊”或限制一個靈活的值,使其處於最小和最大範圍之間。

使用方法如下:

  1. minimum 最小值:例如 16px
  2. flexible 彈性值:例如 5vw
  3. maximum 最大值:例如 34px
h1 {
  font-size: clamp(16px, 5vw, 34px);
}

在此示例中,僅當該值大於 16px 且小於 34px 時, h1 字體大小值將為視口寬度的 5% 。

例如,如果你的視口寬度是 300px ,你的 5vw 值將等於 15px ,但是,你將該字體大小值限製為最小 16px ,因此這就是將要發生的情況。

另一方面,如果你的視口寬度為 1400px ,則 5vw 將高達 70px !但幸運的是,你將該最大值限製為 34px ,因此它不會超過該值。

在線Demo:https://dip15739.github.io/ResponsiveWebsite-CSSproperty/

我可以為此模板添加此代碼…

img {
  width: clamp(15vw, 800%, 100%);
}
h1 {
  font-size: clamp(20px, 5vw, 35px);
}
p {
  font-size: clamp(10px, 4vw, 20px);
}

而從字面上看,接受任何其他長度、頻率、角度、時間、百分比、数字或整數的屬性。

原文:https://dev.to/dip15739/responsive-website-with-only-1-css-property-3ea9

作者:Dip Vachhani

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9818

沐鳴娛樂業務:_前端模塊化簡單總結

前言

JavaScript初期就是為了實現簡單的頁面交互邏輯,如今CPU、瀏覽器性能得到了極大的提升,很多頁面邏輯遷移到了客戶端,前端代碼日益膨脹,此時在js方面就會考慮怎麼樣來管理使用模塊化規範去管理。

無論是什麼語言一旦發展到一定地步,其工程化能力和可維護性勢必得到相應的發展。 模塊化這件事,無論在哪個編程領域都是常見的事情,模塊化存在的意義就是為了增加可復用性,以盡可能少的代碼是實現個性化的需求。同為前端三劍客之一的 css 早在 2.1 的版本就提出了 @import 來實現模塊化,但是 JavaScript 直到 ES6 才出現官方的模塊化方案: ES Module (import、export)。儘管早期 JavaScript 語言規範上不支持模塊化,但這並沒有阻止 JavaScript 的發展,官方沒有模塊化標準開發者們就開始自己創建規範,自己實現規範。

前端模塊化

JavaScript 在早期的設計中就沒有模塊、包甚至類的概念,雖然 ES6 中有了 class 關鍵字,那也只是個語法糖。隨意隨着項目複雜度的增加,開發者必然需要模擬類的功能,來隔離、封裝、組織複雜的 JavaScript 代碼,而這種封裝和隔離,也被被我們稱之為模塊化。

模塊就是一個實現特定功能的文件 or 代碼塊。隨着前端工程體系建設的愈發成熟,或許模塊化的概念已經在前端圈子里已經耳熟能詳了。但是對於很多開發者而言,ES6 中的 export、import,nodejs 中的 require、exports.xx、module.exports到底有什麼區別?為什麼又有 CommonJS,又有 AMD,CMD,UMD?區別是什麼?甚至我們在編寫 ts 文件的時候,還需要在配置文件裏面說明什麼模塊方式,在項目中使用的時候,我們又是否真正知道,你用的到底是基於哪一種規範的模塊化?

模塊化的價值

可維護性,每一個模塊都是獨立的。良好的設計能夠極大的降低項目的耦合度。以便於其能獨立於別的功能被整改。至少維護一個獨立的功能模塊,比維護一坨凌亂的代碼要容易很多。

減少全局變量污染,前端開發的初期,我們都在為全局變量而頭疼,因為經常會觸發一些難以排查且非技術性的 bug。當一些無關的代碼一不小心重名了全局變量,我們就會遇到煩人的“命名空間污染”的問題。在模塊化規範沒有確定之前,其實我們都在極力的避免於此。(後文會介紹)

可復用性,前端模塊功能的封裝,極大的提高了代碼的可復用性。這點應該就不用詳細說明了。想想從 npm 上找 package 的時候,是在幹啥?

方便管理依賴關係,在模塊化規範沒有完全確定的時候,模塊之間相互依賴的關係非常的模糊,完全取決於 js 文件引入的順序。粗俗!絲毫沒有技術含量,不僅依賴模糊且難以維護。

模塊化的進化過程

1、函數封裝

回到我們剛剛說的模塊的定義,模塊就是一個實現特定功能的文件 or 代碼塊(這是我自己給定義的)。專業定義是,在程序設計中,為完成某一功能所需的一段程序或子程序;或指能由編譯程序、裝配程序等處理的獨立程序單位;或指大型軟件系統的一部分。而函數的一個功能就是實現特定邏輯的一組語句打包。並且 JavaScript 的作用域就是基於函數的,所以最原始之處,函數必然是作為模塊化的第一步。

將不同的功能封裝成不同的函數

編碼: 將不同的功能封裝成不同的全局函數

問題: 污染全局命名空間, 容易引起命名衝突或數據不安全,而且模塊成員之間看不出直接關係,模塊之間的關係模糊

//函數1
function fn1(){
  //statement
}
//函數2
function fn2(){
  //statement
}

2、namespace模式 

也可以理解為對象封裝,其實就是把相關函數、變量在外面加了一層

let module1 = {
  let tag : 1,
  let name:'module1',
  
  fun1(){
    console.log('this is fun1')
  },
  
  fun2(){
    console.log('this is fun2')
  }
}

我們在使用的時候呢,就直接

module1.fun2();

優點

一定程度上優化了命名衝突,降低了全局變量污染的風險

有一定的模塊封裝和隔離,並且還可以進一步語義化一些

缺點

並沒有實質上改變命名衝突的問題

外部可以隨意修改內部成員變量,還是容易產生意外風險

 

3、IIFE模式:立即執行匿名函數(閉包)

let global = 'Hello, I am a global variable :)';

(function () {
  // 在函數的作用域中下面的變量是私有的

  const myGrades = [93, 95, 88, 0, 55, 91];

  let average = function() {
    let total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item}, 0);

    return 'Your average grade is ' + total / myGrades.length + '.';
  }

  let failing = function(){
    let failingGrades = myGrades.filter(function(item) {
      return item < 70;});

    return 'You failed ' + failingGrades.length + ' times.';
  }

  console.log(failing());
  console.log(global);

// 需要暴露的api
return {
// something
} }()); // 控制台显示:'You failed 2 times.' // 控制台显示:'Hello, I am a global variable :)'

這種方法的好處在於,你可以在函數內部使用局部變量,而不會意外覆蓋同名全局變量,但仍然能夠訪問到全局變量

類似如上的 IIFE ,還有非常多的演進寫法

比如引入依賴:

把內部需要的變量傳進去。

// module.js文件
(function(window, $) {
  let data = 'www.baidu.com'
  //操作數據的函數
  function foo() {
    //用於暴露有函數
    console.log(`foo() ${data}`)
    $('body').css('background', 'red')
  }
  function bar() {
    //用於暴露有函數
    console.log(`bar() ${data}`)
    otherFun() //內部調用
  }
  function otherFun() {
    //內部私有的函數
    console.log('otherFun()')
  }
  //暴露行為
  window.myModule = { foo, bar }
})(window, jQuery)

使用

 // index.html文件
  <!-- 引入的js必須有一定順序 -->
  <script type="text/javascript" src="jquery-1.10.1.js"></script>
  <script type="text/javascript" src="module.js"></script>
  <script type="text/javascript">
    myModule.foo()
  </script>

優點

實現了基本的封裝

只暴露對外的方法操作,利用閉包實現了類似 public 和 private 的概念

缺點

模塊依賴關係模糊

模塊與模塊之間不好管理

上述的所有解決方案,雖然每種方法都可行,但是始終沒有哪一種可以很好的解決變量污染、各個模塊之間依賴關係清晰、方便管理、與第三方代碼方便集成。隨着大前端時代的到來,在2009年提出了 CommonJS 規範,並且nodeJs 直接用該規範來管理模塊化,隨着時間的遷移,現在 JavaScript 模塊規範也就有了:CommonJS、AMD、CMD、UMD、ES6 模塊化。

4、CommonJS

CommonJS 是 JavaScript 的一個模塊化規範(http://www.commonjs.org/),主要用於服務端Nodejs 中。根據規範,每一個文件既是一個模塊,其內部定義的變量是屬於這個模塊的,不會污染全局變量。每個模塊內部,module變量代表當前模塊,這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。加載某個模塊,其實是加載該模塊的module.exports屬性。

CommonJS 的核心思想是通過 require 方法來同步加載所依賴的模塊,然後通過 exports 或者 module.exprots 來導出對外暴露的接口。 

基本用法

暴露模塊:module.exports = value 或 exports.xxx = value

引入模塊:require(xxx),如果是第三方模塊,xxx為模塊名;如果是自定義模塊,xxx為模塊文件路徑

// example.js
let x = 5;
let addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
let example = require('./example.js');
console.log(example.x); // 5
console.log(example.addX(1)); // 6

require命令用於加載模塊文件。require命令的基本功能是,讀入並執行一個JavaScript文件,然後返回該模塊的exports對象,如果沒有發現指定模塊,會報錯,如果存在多個 exports  只有第一個exports 有效

require 是加載這個文件然後執行,在返回、輸出 exports 這個對象

console.log('開始加載了') // 會輸出 開始加載了

function run (val) {
  console.log(val)
}

特點

以文件為一個單元模塊,代碼運行在模塊作用域內,不會污染全局變量

同步加載模塊,在服務端直接讀取本地磁盤沒問題,不太適用於瀏覽器

模塊可以加載多次,但是只會在第一次加載時運行,然後在加載,就是讀取的緩存文件。需清理緩存后才可再次讀取文件內容

模塊加載的順序,按照其在代碼中出現的順序

導出的是值的拷貝,這一點和 ES6 有着很大的不同(後面會介紹到)

補充知識點

Node 中,一個文件是一個模塊->module

源碼定義如下:

function Module(id = '', parent) {
  this.id = id;
  this.path = path.dirname(id);
  this.exports = {};
  this.parent = parent;
  updateChildren(parent, this, false);
  this.filename = null;
  this.loaded = false;
  this.children = [];
}
//實例化一個模塊
var module = new Module(filename, parent);

CommonJS 的一個模塊,就是一個腳本文件。require命令第一次加載該腳本,就會執行整個腳本,然後在內存生成一個對象。

{
  id: '...',
  exports: { ... },
  loaded: true,
  ...
}

上面代碼就是 Node 內部加載模塊後生成的一個對象。該對象的id屬性是模塊名,exports屬性是模塊輸出的各個接口,loaded屬性是一個布爾值,表示該模塊的腳本是否執行完畢。其他還有很多屬性。以後需要用到這個模塊的時候,就會到exports屬性上面取值。即使再次執行require命令,也不會再次執行該模塊,而是到緩存之中取值。也就是說,CommonJS 模塊無論加載多少次,都只會在第一次加載時運行一次,以後再加載,就返回第一次運行的結果,除非手動清除系統緩存。

關於AMD、CMD

CommonJS 在 Node.js 環境下取得了很不錯的成功,很多人想把commonJs 規範推向瀏覽器端,但是瀏覽器不能像服務器那樣直接讀取磁盤上的內容所以又有了後面的AMD、CMD規範。ES6 在語言標準的層面上,實現了模塊功能,而且實現得相當簡單,完全可以取代現有的 CommonJS 和 AMD 規範,成為瀏覽器和服務器通用的模塊解決方案,因為我自己也是在近幾年才做前端, AMD、CMD 並沒有太多使用,所以AMD、CMD 這裏只是做簡單的介紹。

5、AMD

AMD 全稱為 Asynchromous Module Definition(異步模塊定義)

AMD 是 RequireJS 在推廣過程中對模塊定義的規範化產出,它是一個在瀏覽器端模塊化開發的規範。 AMD 模式可以用於瀏覽器環境並且允許異步加載模塊,同時又能保證正確的順序,也可以按需動態加載模塊。

特點

異步加載模塊,不會造成因網絡問題而出現的假死

顯式地列出其依賴關係,並以函數(定義此模塊的那個函數)參數的形式將這些依賴進行注入

在模塊開始時,加載所有所需依賴

定義模塊

define(id?: String, dependencies?: String[], callback: Function|Object);

id,一個可選參數,類似給模塊取個名字,但是卻是模塊的唯一標識。如果沒有提供則取腳本的文件名

dependence,依賴的模塊數組

callback,工廠方法,模塊初始化的一些操作。如果是函數,只被執行一次。如果是對象,則為模塊的輸出值

使用模塊

require([moduleName],callback);

使用

//article.js文件
// 定義有依賴的模塊
define(['user'], function(user) {
  let name = 'THE LAST TIME'
  function consoleMsg() {
    console.log(`${name} by ${user.getAuthor()}`);
  }
  // 暴露模塊
  return { consoleMsg }
})
// 調用 article 模塊種的 consoleMsg
require(['article'], function(alerter) { article.consoleMsg() })

關於 require.js 的使用,仔細看文檔,其實還是有很多知識點的。但是鑒於我們着實現在使用不多(我也不熟),所以這裏也就參考網上優秀文章和自己實踐,拋磚引玉。

6、CMD

CMD 即Common Module Definition通用模塊定義,CMD 是 SeaJS 在推廣過程中對模塊定義的規範化產出,是阿里的玉伯提出來,它和 AMD 其實非常的相似,文件即為模塊。CMD最主要的區別是實現了按需加載,推崇依賴就近的原則,模塊延遲執行,而 AMD 所依賴模塊式提前執行(requireJS 2.0 后也改為了延遲執行)   所以AMD和CMD最大的區別是對依賴模塊的執行時機處理不同,注意不是加載的時機或者方式不同。CMD 規範盡量保持簡單,並與 CommonJS 規範保持了很大的兼容性。通過 CMD 規範書寫的模塊,可以很容易在 Node.js 中運行。 在 CMD 規範中,一個模塊就是一個文件。格式如下:

define(factory);

define 是一個全局函數,用來定義模塊,參數factory可以是對象、字符串、函數

factory 為對象、字符串時,表示模塊的接口就是該對象、字符串。比如可以如下定義一個 JSON 數據模塊:

define({ "foo": "bar" });

也可以通過字符串定義模板模塊:

define('I am a template. My name is {{name}}.');

factory 為函數時,表示是模塊的構造方法。

執行該構造方法,可以得到模塊向外提供的接口。factory 方法在執行時,默認會傳入三個參數:require、exports 和 module:

define(function(require, exports, module) {
  // 模塊代碼
});

使用sea.js

/** sea.js **/
// 定義模塊 math.js
define(function(require, exports, module) {
    var $ = require('jquery.js');
    var add = function(a,b){
        return a+b;
    }
    exports.add = add;
});
// 加載模塊
seajs.use(['math.js'], function(math){
    var sum = math.add(1+2);
});

關於 sea.js 的使用,仔細看文檔,其實還是有很多知識點的。但是鑒於我們着實現在使用不多(我也不熟),所以這裏也就參考網上優秀文章和自己實踐,拋磚引玉。

 7、UMD

UMD 是 AMD 和 CommonJS 的綜合產物。如上所說,AMD 的用武之地是瀏覽器,非阻塞式加載。CommonJS 主要用於服務端 Nodejs 中使用。所以人們就想到了一個通用的模式 UMD(universal module definition)。來解決跨平台的問題。
沒錯!就是 ifElse 的寫法。
核心思想就是:先判斷是否支持Node.js的模塊(exports)是否存在,存在則使用Node.js模塊模式。
在判斷是否支持AMD(define是否存在),存在則使用AMD方式加載模塊。

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        //AMD
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        //Node, CommonJS之類的
        module.exports = factory(require('jquery'));
    } else {
        //瀏覽器全局變量(root 即 window)
        root.returnExports = factory(root.jQuery);
    }
}(this, function ($) {
    //方法
    function myFunc(){};
    //暴露公共方法
    return myFunc;
}));

8、ES Module

  在 ES Module 之前,社區制定了一些模塊加載方案,最主要的有 CommonJS 和 AMD 兩種。前者用於服務器,後者用於瀏覽器。ES Module 在語言標準的層面上,實現了模塊功能,而且實現得相當簡單,完全可以取代 CommonJS 和 AMD 規範,成為瀏覽器和服務器通用的模塊解決方案。

ES Module 的設計思想是盡量的靜態化,使得編譯時就能確定模塊的依賴關係,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時確定這些東西。

  CommonJS 和 AMD 模塊,其本質是在運行時生成一個對象進行導出,稱為“運行時加載”,沒法進行“編譯優化”,而 ES Module 不是對象,而是通過 export 命令顯式指定輸出的代碼,再通過 import 命令輸入。這稱為“編譯時加載”或者靜態加載,即 ES Module 可以在編譯時就完成模塊加載,效率要比 CommonJS 模塊的加載方式高。當然,這也導致了沒法引用 ES Module 模塊本身,因為它不是對象。

由於 ES Module 是編譯時加載,使得靜態分析成為可能。有了它,就能進一步拓寬 JavaScript 的語法,比如引入宏(macro)和類型檢驗(type system)這些只能靠靜態分析實現的功能。

特點

靜態編譯

輸出的值引用,而非值拷貝

import 只能寫在頂層,因為是靜態語法

9、CommonJs、ESM 區別

CommonJs ES6 Module
運行時加載;CommonJs模塊就是對象(module.exports屬性)),即在輸入時是先加載整個模塊、執行模塊,生成一個對象,然後再從這個對象上面讀取方法。 編譯時加載;ES6 模塊不是對象,而是通過 export 命令顯式指定輸出的代碼,import時採用靜態命令的形式。即在import時可以指定加載某個輸出值,而不是加載整個模塊。
輸出的是值的拷貝(一旦輸出一個值,模塊內部的變化就影響不到這個值。) 輸出的是值的引用(JS 引擎對腳本靜態分析的時候,遇到模塊加載命令import,就會生成一個只讀引用。等到腳本真正執行時,再根據這個只讀引用,到被加載的那個模塊裏面去取值。即原始值變了,import加載的值也會跟着變。因此,ES6 模塊是動態引用,並且不會緩存值,模塊裏面的變量綁定其所在的模塊。)


差異

CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。

CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。

加載 & 編譯

因為 CommonJS 加載的是一個對象(module.exports),對象只有在有腳本運行的時候才能生成。而 ES6 模塊不是一個對象,只是一個靜態的定義。在代碼解析階段就會生成。

ES6 模塊是編譯時輸出接口,因此有如下2個特點:

import 命令會被 JS 引擎靜態分析,優先於模塊內的其他內容執行

export 命令會有變量聲明提升的效果,所以import 和 export 命令在模塊中的位置並不影響程序的輸出。

異步加載模塊,不會造成因網絡問題而出現的假死

顯式地列出其依賴關係,並以函數(定義此模塊的那個函數)參數的形式將這些依賴進行注入

在模塊開始時,加載所有所需依賴

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9827

沐鳴註冊平台_ECMAScript7規範中的instanceof操作符

本文主要講解ECMAScript7規範中的instanceof操作符。

預備知識

有名的Symbols

“有名”的Symbols指的是內置的符號,它們定義在Symbol對象上。ECMAScript7中使用了@@name的形式引用這些內置的符號,比如下面會提到的@@hasInstance,其實就是Symbol.hasInstance。

InstanceofOperator(O, C)

O instanceof C在內部會調用InstanceofOperator(O, C)抽象操作,該抽象操作的步驟如下:

  1. 如果C的數據類型不是對象,拋出一個類型錯誤的異常;
  2. 讓instOfHandler等於GetMethod(C, @@hasInstance),大概語義就是獲取對象C的@@hasInstance屬性的值;
  3. 如果instOfHandler的值不是undefined,那麼:

    1. 返回ToBoolean(? Call(instOfHandler, C, « O »))的結果,大概語義就是執行instOfHandler(O),然後把調用結果強制轉化為布爾類型返回。
  4. 如果C不能被調用,拋出一個類型錯誤的異常;
  5. 返回OrdinaryHasInstance(C, O)的結果。

OrdinaryHasInstance(C, O)

OrdinaryHasInstance(C, O)抽象操作的步驟如下:

  1. 如果C不能被調用,返回false;
  2. 如果C有內部插槽[[BoundTargetFunction]],那麼:

    1. 讓BC等於C的內部插槽[[BoundTargetFunction]]的值;
    2. 返回InstanceofOperator(O, BC)的結果;
  3. 如果O的類型不是對象,返回false;
  4. 讓P等於Get(C, “prototype”),大概語義是獲取C.prototype的值;
  5. 如果P的數據類型不是對象,拋出一個類型錯誤的異常;
  6. 重複執行下述步驟:

    1. 讓O等於O.[[GetPrototypeOf]]()的結果,大概語義就是獲取O的原型對象;
    2. 如果O等於null,返回false;
    3. 如果SameValue(P, O)的結果是true,返回true。

SameValue抽象操作參見JavaScript中的==,===和Object.js()中的Object.is(),Object.is()使用的就是這個抽象操作的結果。

由上述步驟2可知,如果C是一個bind函數,那麼會重新在C綁定的目標函數上執行InstanceofOperator(O, BC)操作。

由上述步驟6可知,會重複地獲取對象O的原型對象,然後比較該原型對象和C的prototype屬性是否相等,直到相等返回true,或者O變為null,也就是遍歷完整個原型鏈,返回false。

Function.prototype[@@hasInstance] (V)

由上面的InstanceofOperator(O, C)抽象操作的步驟2和3可以知道,如果C上面定義或繼承了@@ hasInstance屬性的話,會調用該屬性的值,而不會走到步驟4和5。步驟4和5的目的是為了兼容沒有實現@@hasInstance方法的瀏覽器。如果一個函數沒有定義或繼承@@hasInstance屬性,那麼就會使用默認的instanceof的語義,也就是OrdinaryHasInstance(C, O)抽象操作描述的步驟。

ECMAScript7規範中,在Function的prototype屬性上定義了@@hasInstance屬性。Function.prototype[@@hasInstance](V)的步驟如下:

  1. 讓F等於this值;
  2. 返回OrdinaryHasInstance(F, V)的結果。

所以,你可以看到在默認情況下,instanceof的語義是一樣的,都是返回OrdinaryHasInstance(F, V)的結果。為什麼說默認情況下?因為你可以覆蓋Function.prototype[@@hasInstance]方法,去自定義instanceof的行為。

例子

function A () {}
function B () {}

var a = new A
a.__proto__ === A.prototype // true
a.__proto__.__proto__ === Object.prototype // true
a.__proto__.__proto__.__proto__ === null // true

a instanceof A // true
a instanceof B // false

由OrdinaryHasInstance(C, O)的第6步可知:

  • 對於a instanceof A,P是A.prototype,在第一次循環的時候,a的原型對象a._proto__是A.prototype,也就是步驟中的O是A.prototype,所以返回了true;
  • 對於a instanceof B,P是B.prototype,在第一次循環的時候,a的原型對象a._proto__是A.prototype,不等於P;執行第二次循環,此時O是a.__proto__.__proto__,也就是Object.prototype,不等於P;執行第三次循環,此時O是a.__proto__.__proto__.__proto__,也就是null,也就是原型鏈都遍歷完了,所以返回了false。

接着上面的例子:

A.prototype.__proto__ = B.prototype

a.__proto__ === A.prototype // true
a.__proto__.__proto__ === B.prototype // true
a.__proto__.__proto__.__proto__ === Object.prototype // true
a.__proto__.__proto__.__proto__.__proto__ === null // true

a instanceof B // true

在上面的例子中,我們把B.prototype設置成了a的原型鏈中的一環,這樣a instanceof B在OrdinaryHasInstance(C, O)的第6步的第2次循環的時候,返回了true。

由OrdinaryHasInstance(C, O)的第2步,我們知道bind函數的行為和普通函數的行為是不一樣的:

function A () {}
var B = A.bind()

B.prototype === undefined // true

var b = new B
b instanceof B // true
b instanceof A // true

由上面的例子可知,B.prototype是undefined。所以,instanceof作用於bind函數的返回結果其實是作用於綁定的目標函數的返回值,和bind函數基本上沒有什麼關係。

由InstanceofOperator(O, C)步驟2和步驟3可知,我們可以通過@@hasInstance屬性來自定義instanceof的行為:

function A () {}
var a = new A
a instanceof A // true

A[Symbol.hasInstance] = function () { return false }
a instanceof A // ?

在chrome瀏覽器測試了一下,發現還是輸出true。然後看了一下ECMAScript6的文檔,ECMAScript6文檔裏面還沒有規定可以通過@@hasInstance改變instanceof的行為,所以應該是目前chrome瀏覽器還沒有實現ECMAScript7中的instanceof操作符的行為。

直到有一天看了MDN上Symbol.hasInstance的兼容性部分,發現chrome從51版本就開始支持Symbol.hasInstance了:

class MyArray {  
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance)
  }
}
console.log([] instanceof MyArray) // true

那麼為什麼我那樣寫不行呢?直到我發現:

function A () {}
var fun = function () {return false}
A[Symbol.hasInstance] = fun
A[Symbol.hasInstance] === fun // false
A[Symbol.hasInstance] === Function.prototype[Symbol.hasInstance] // true
A[Symbol.hasInstance] === A.__proto__[Symbol.hasInstance] // true

由上面的代碼可知,A[Symbol.hasInstance]並沒有賦值成功,而且始終等於Function.prototype[Symbol.hasInstance],也就是始終等於A的原型上的Symbol.hasInstance方法。那是不是因為原型上的同名方法?

Object.getOwnPropertyDescriptor(Function.prototype, Symbol.hasInstance)
// Object {writable: false, enumerable: false, configurable: false, value: function}

由上面的代碼可知,Function.prototype上的Symbol.hasInstance的屬性描述符的writable是false,也就是這個屬性是只讀的,所以在A上面添加Symbol.hasInstance屬性失敗了。但是為啥沒有失敗的提示呢?

'use strict'
function A () {}
var fun = function () {return false}
A[Symbol.hasInstance] = fun
// Uncaught TypeError: Cannot assign to read only property 'Symbol(Symbol.hasInstance)' of function 'function A() {}'

錯誤提示出來了,所以以後還是盡量使用嚴格模式。非嚴格模式下有些操作會靜默失敗,也就是即使操作失敗了也不會有任何提示,導致開發人員認為操作成功了。

var a = {}
a[Symbol.hasInstance] = function () {return true}
new Number(3) instanceof a // true

因為可以通過自定義Symbol.hasInstance方法來覆蓋默認行為,所以用instanceof操作符判斷數據類型並不一定是可靠的。

還有一個問題:為什麼上面MDN文檔的例子可以成功,我最初的例子就不行呢,目的不都是寫一個構造函數,然後在構造函數上添加一個屬性嗎?
個人分析的結果是:雖然大家都說Class是寫構造函數的一個語法糖,但是其實還是和使用function的方式有差別的,就比如上面的例子。使用Class的時候,會直接在構造函數上添加一個靜態屬性,不會先檢查原型鏈上是否存在同名屬性。而使用function的方式的時候,給構造函數添加一個靜態方法,相當於給對象賦值,賦值操作會先檢查原型鏈上是否存在同名屬性,所以就會有賦值失敗的風險。所以,就給構造函數添加Symbol.hasInstance屬性來說,Class能做到,使用Function的方式就做不到。

更新於2018/11/20
上面總結到

所以,就給構造函數添加Symbol.hasInstance屬性來說,Class能做到,使用Function的方式就做不到。

但是,後來發現給對象添加屬性的方法不只是賦值這一種方式,還有一個Object.defineProperty方法:

function A () {}
var a = new A
a instanceof A // true

Object.defineProperty(A, Symbol.hasInstance, {
    value: function () { return false }
})
a instanceof A // false

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9822

沐鳴平台註冊登錄_將偽數組轉換為數組的 N 種方案

今天面試了一個人,居然不知道如何將偽數組轉換為數組

什麼是偽數組?

有 length 屬性,而且也是數值下標的對象。

不具備 Array.prototype 上的方法

常見偽數組

arguments

document.getElementsByClassName

$(‘div’)

偽數組轉換為數組

輸出偽數組

function fun(a,b,c = 1){
    arr = arguments
    console.log(
        typeof arr,
        Array.isArray(arr),
        arr.length,
        arr.slice,
        arr,
    )
fun(3, 2)

使用 Array.from (ES6+)(babel-polyfill)

function fun(a,b,c = 1){
    arr = Array.from(arguments)
    console.log(
        typeof arr,
        Array.isArray(arr),
        arr.length,
        arr.slice,
        arr,
    )
fun(3, 2)

使用 … 展開運算符(ES6+)(babel)

function fun(a,b,c = 1){
    arr = [...arguments]
    console.log(
        typeof arr,
        Array.isArray(arr),
        arr.length,
        arr.slice,
        arr,
    )
fun(3, 2)

使用 slice 和 call 的方案

function fun(a,b,c = 1){
    arr = Array.prototype.slice.call(arguments)
    console.log(
        typeof arr,
        Array.isArray(arr),
        arr.length,
        arr.slice,
        arr,
    )
    arr = Array.prototype.slice.apply(arguments)
    console.log(
        typeof arr,
        Array.isArray(arr),
        arr.length,
        arr.slice,
        arr,
    )
    arr = [].slice.call(arguments)
    console.log(
        typeof arr,
        Array.isArray(arr),
        arr.length,
        arr.slice,
        arr,
    )
    arr = [].slice.apply(arguments)
    console.log(
        typeof arr,
        Array.isArray(arr),
        arr.length,
        arr.slice,
        arr,
    )
}
fun(3, 2)

循環遍歷(兼容性無敵,樸素不)

function fun(a,b,c = 1){
    arr = [];
    for(var i = 0,length = arguments.length; i < length; i++) {
        arr.push(arguments[i]);
    }
    console.log(
        typeof arr,
        Array.isArray(arr),
        arr.length,
        arr.slice,
        arr,
    )
}
fun(3, 2)

微信公眾號:前端linong

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9787

沐鳴註冊_簡單的useState實現

以下是一段非常簡單的 react 代碼:

const App = () => {
  const [n, setN] = useState(0);
  return (
    <div>
      {n}
      <button onClick={() => setN(x => x + 1)}>+1</button>
    </div>
  );
}

react.render(<App />, rootElement)

這樣的用法和以往的 setState 是有明顯的不同的,他看起來更像 redux——我們初始化一個 state,然後 dispatch 一個 action,再由 reducer 改變 state 后返回新的 state。

Redux 思想實現 useState

既然我們覺得它像,那我們就來自己實現一個吧。

不熟悉 Redux 思想的同學請自行閱讀文檔

const useState = (initialValue) => {
  let state = initialValue;
  const dispatch = (newState) => {
    state = newState;
    render(<App />, document.getElementById('root'));
  };
  return [state, dispatch];
};

然後我們用這個自定義的 useState 代替 React 的 useState——就會發現我們失敗了,setN 無論如何都不會有任何反應。

這是因為我們每次重新 render 的時候都重新執行了函數,於是我們總是會重新賦值

為什麼不會重新賦值?

對於這段 React 代碼來說,當我們第一次運行時,React 會進行首次渲染,即 render(<App />, …)。

在此過程中,會先調用 App(),之後便會得到虛擬 DOM,再創建真實的 Div。

當我們觸發點擊事件時,會調用 setN,再次 render()。之後調用 App(),然後得到新的虛擬 DOM,進行 diff 算法,根據 diff 算法的結果去更新新的 Div。

而不管是第一次渲染,還是第二次調用,都會調用 useState()。

但是我們寫的是 useState(0) 啊,兩次調用明明是一樣的代碼,為何 n 的值不同?怎麼解決這個問題呢?

很簡單,閉包嘛。

const createUseState = () => {
  let state;
  const useState = (initialValue) => {
    if (!state) {
      state = initialValue;
    }
    const dispatch = (newState) => {
      state = newState;
      render(<App />, document.getElementById('root'));
    };
    return [state, dispatch];
  };
};

這樣就解決了重新賦值的問題。

多次調用

但是我們需要多次調用 useState 呀,不可能只用一次的。

於是我們將 state 改為一個數組:const state = [];。

const createUseState = () => {
  const state = [];
  let index = 0;
  return (initialValue) => {
    state[index] = state[index] || initialValue;
    const currentIndex = index;
    const dispatch = (newState) => {
      state[currentIndex] = newState;
      // 重點
      index = 0;
      ReactDOM.render(
        <React.StrictMode>
          <App />
        </React.StrictMode>,
        rootElement
      );
    };
    return [state[index++], dispatch];
  };
};

我們創建了一個 index 變量來控制索引。它需要我們保證每次重新渲染 App 傳入數組的元素是一樣的——這就是為什麼我們不可以將 useState 寫在 if 判斷中。

在上述代碼中有一處重點,在於我們需要在每次 set 之後將索引歸零 index = 0。

因為每次 render 結束后,React 都會重新執行該函數。

作者:幾乎一米八的徐某某

出處:Aero Blog (https://www.cnblogs.com/xhyccc/)


站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9761

沐鳴娛樂_使用 Typescript 開發 Nodejs 命令行工具

本文記錄了搭建基於 TypeScript 的 Nodejs 命令行開發環境的全過程。

為何使用TypeScript

首先,對於編寫類庫或者工具而言,使用 TypeScript 的最大好處就是其提供了類型機制,可以避免我們犯一些低級錯誤。

其次,配合編輯器(如 VS Code),TypeScript 能提供強大的代碼提示功能,我們不需要記憶很多API的具體使用,在編寫代碼時編輯器會自動進行提示。比如引入了 http 之後,輸入 http. 就會提示可以使用的各個方法和屬性,並給出詳細的說明。

同是微軟旗下,VS Code 具有非常強大便利的功能,強烈推薦使用 VS Code 進行 TypeScript 和 Nodejs 開發。

最後,使用 TypeScript 是大勢所趨,很多大公司都在推 TypeScript,使用 TypeScript 開發,可以讓我們對 TS 使用更加熟練。

初始化工程

建立命令行工具,需要先創建一個 npm 包。下文將使用 npm 工具來完成包的初始化和依賴的安裝。

首先創建一個文件夾,然後運行初始化命令:

mkdir ts-node-demo && cd ts-node-demo
npm init

控制台會出現一系列提示, 按照需求輸入即可,然後一路回車,完成之後輸入 yes 。

package name: (typescript-cli) 
version: (1.0.0) 
description: a cli in typescript
entry point: (index.js) 
test command: 
git repository: 
keywords: CLI,TypeScript
author: YourName
license: (ISC) MIT

初始化之後本地文件夾會出現一個 package.json 文件。我們的 npm 包就已經初始化完成了。為了避免誤發布,我們在 package.json 中做一個更改:

- private: false,
+ private: true,

初始化 Git

在當前目錄下運行:

git init

然後在當前目錄創建 .gitignore 文件,指定忽略 node_modules 文件夾:

node_modules/
lib/

引入 Node 類型

既然是開發 Nodejs 程序,為了獲得合適的類型校驗和代碼提示,我們需要引入 Nodejs 的類型文件:

npm i -D @types/node

引入 typescript

npm i typescript

然後需要初始化 tsconfig 文件。

./node_modules/.bin/tsc --init

上述命令會在當前文件夾下面創建一個 tsconfig 文件,用來指導 TypeScript 進行編譯。 在裏面有非常多的配置項,並且有非常詳細的解釋,我們做兩個更改來適配我們的項目:

+ "sourceMap": true,
+ "outDir": "lib",

上述配置指定生成 sourceMap 文件,並將 TypeScript 的編譯結果輸出到 ./lib 文件夾.

然後在與 compilerOptions 平級的地方增加選項:

"compilerOptions": {
    ...
},
+ "include": [
+    "src/**/*"
+ ]

這表示我們只會編譯 src 目錄下的 .ts 文件。

編寫代碼

在當前目錄下創建 src 文件夾,並創建 index.ts:

mkdir src && echo "console.log('Your cli is running.');" > src/index.ts

然後運行:

./node_modules/.bin/tsc 

可以發現在文件夾下出現了 lib/ 目錄,裏面就是 index.ts 編譯之後的 js 文件。

創建運行腳本

每次編譯都需要引用 node_modules 裏面的 tsc 命令,有些繁瑣,有三種方法可以解決:

全局安裝 typescript 包:

npm i typescript -g

就可以直接使用 tsc 命令了。

使用 npx 執行 npx 是 npm 提供的命令,其會自動下載對應的包並執行.

npx tsc

創建 npm 腳本 在 package.json 中的 script 中增加一行腳本:

"script": {
+    "build": "tsc"
}

這裏我們採用第3種方法,寫入腳本后可以執行:

npm run build

也會成功進行編譯。

註冊命令

開發 Nodejs 命令行工具,就是提供一個可以直接調用的命令,而不是使用下面這種方式執行文件:

node lib/index.js

我們想要的效果是執行一個命令就能調用我們的 js 文件。

首先在當前文件夾創建文件 bin/node-cli-demo :

mkdir bin && touch bin/node-cli-demo.js

然後在文件中寫入以下內容:

#!/usr/bin/env node
require('../lib/index.js');

npm 包註冊的命令需要在 package.json 中進行聲明,增加如下內容:

{
    "name": "typescript-cli",
    "version": "0.0.1",
+   "bin": {
+       "node-cli-demo": "./bin/node-cli-demo.js"
+   },
}

這表示當執行 node-cli-demo 這個命令時就去執行我們的 ./bin/node-cli-demo.js 文件。

最後在當前目錄調用 npm link ,這條命令會把我們本地註冊的命令放到 Nodejs 安裝目錄的 bin 文件夾下。在安裝 Nodejs 時系統將該文件夾添加到命令查找的路徑中。所以我們就可以直接使用我們剛剛註冊的命令:

node-cli-demo
// Your cli is running.

自動監聽文件變動

我們希望每次更改了 .ts 文件之後,不必手動執行 npm run build 就能看到最新的效果,可以使用 typescript 的 –watch 選項,在 package.json 中的 script 中增加 start 命令:

{
    "script": {
+       "start": "tsc --watch"
    }
}

在當前目錄下運行命令:

npm start

然後對 src/index.ts 文件做一些更改,另開一個控制台窗口,運行 node-cli-demo,會發現打印的內容已經更新了。 這樣我們在開發時就只需要關注代碼編寫,而不用考慮編譯的問題了。

接下來我們就可以在 src 文件裏面寫我們的具體代碼了!

注: 本文的 demo 代碼可以在 github 上查看。為了避免創建很多倉庫,我將其放到了一個倉庫的子目錄裏面。

總結

使用 TypeScript 開發 Nodejs 命令行的流程如下:

安裝 typescript 並進行配置;

在 package.json 中聲明命令並使用 npm link 將其鏈接到全局命令中;

使用 tsc –watch 自動監聽文件變動並重新編譯;

運行註冊過的命令,查看效果。

配置ESLint

使用 ESLint 校驗我們的代碼,可以避免一些低級錯誤。而 TypeScript 現在推薦採用 ESLint 來檢查代碼。我們可以為我們的工程配置 ESLint。

安裝依賴

首先安裝依賴:

npm i -D eslint @typescript-eslint/parser  @typescript-eslint/eslint-plugin

@typescript-eslint/parser 是用來解析 TypeScript 代碼的,類似於 @babel/parser;

@typescript-eslint/eslint-plugin 是 ESLint 插件,用來配置具體的檢查規則。

設置配置

在根目錄下創建 .eslintrc, 寫入以下內容:

{
    "root": true,
    "parser": "@typescript-eslint/parser",
    "plugins": [
        "@typescript-eslint"
    ],
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/eslint-recommended",
        "plugin:@typescript-eslint/recommended"
    ],
    "rules": {
        "no-console": "error"
    },
    // set eslint env
    "env": {
        "node": true
    }
}

root: true 表示當前目錄就是尋找 ESLint 的最終配置,這樣 ESLint 就不會再往上層尋找配置;

parse 指定了使用 @typescript-eslint/parser 來解析我們的 TypeScript 代碼;

plugins 指定使用的 ESLint 插件;

extends 指定了額外使用的規則配置。插件指定了一系列自定義的規則配置,只有在 extends 中指定才會生效。

rules 中可以擴展我們自己的規則。

env 中可以指定我們代碼運行的環境,這樣就可以自動判斷某些代碼是不是有錯誤。比如上述配置了 node: true ,我們在使用 require 的時候就不會報錯了。

運行校驗命令

在 package.json 的 script 中寫入:

{
    "script": {
        "lint": "eslint ./src --ext .ts"
    }
}

上述命令指定了對 ./src 目錄下擴展名為 .ts 的文件進行校驗。

然後運行 npm run lint,會發現控制台出現報錯,我們的 ESLint 已經生效了。

配置 VSCode 的 ESLint 插件,編輯器在開發時就會自動提示有問題的代碼,幫助我們編寫符合規範的代碼。

忽略某些文件

我們可以指定某些目錄下的文件不進行校驗,在當前目錄下創建 .eslintignore ,類似 .gitignore,然後在裏面寫入需要忽略的目錄或文件:

node_modules

至此,ESLint 也配置完成了!

以上就是搭建 Nodejs 命令行的 TypeScript 開發環境的全部內容了,希望能幫到大家~

來自:https://zhuqingguang.github.io/2020/11/07/nodejs-cli-with-typescript/

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9831

沐鳴總代理_一個好的web前端開發者,是怎麼學習的?

IT 行業的變化快是眾人皆知的,需要持續去學習新的知識內容。但是,往往我們工作之後,經常發現學習的東西很少了,學習效率非常低,感覺自己到了一個瓶頸期,久而久之,就演變成『一年工作經驗,重複去用十年』的怪圈。

不管你是已經工作了,還是正在學習中的初學者,如果你想在web前端行業中有更高更遠的發展,請用五分鐘閱讀下面的文章。

一、低效率的學習壞習慣

1.被動反覆閱讀

通常一個新手在學習web前端開發的時候,往往會選擇買書去學習,首先這樣的學習效率是非常差的,比如在學習html,css的時候,是完全不用看書的。書上大多數都是理論知識,你並不知道哪裡是重點,所以每個人地方你都會去看,但是事實往往是大多數東西你都不理解。這種學習方式是非常不可取的,你沒有那麼多時間去這麼干。

2 、看書看視頻中代碼示例認為自己就理解了,從不動手編程

這是新手在學習web前端的時候常見的問題,也是大忌,很多人都說,我課程聽懂了,但是自己不會動手寫,這首先就是學習方法的錯誤,這是新手學編程的大忌,不去動手寫,不去跟編譯器和開發環境做鬥爭,你永遠不知道軟件開發過程中的糟糕事情。

 3 、拖延

既然你選擇了學習web前端,就應該知道,我們這行需要不斷去學習新的東西,拖延會讓你成為一個真正的“碼農”。這也是影響N多人不去學習的理由。解決的辦法只有一個,馬上去做!一旦你開始去做了,你就會發現一切沒有那麼難。你的計劃再完美,你選的書籍再經典,你挑的視頻水平再高,如果你不馬上去看,去學,去動手實踐,那永遠也只是停留在空想的階段。成功學習的典範就是成功戰勝拖延症的典範。

4.喜歡自己閉門造車

學習專業知識,不是都靠自己頑強的意志,更多的是需要跟別人交流,重要的就是跟比你強的人交流,加一些氛圍比較好的交流學習群,或者別人的一句話就能讓你茅塞頓開,學技術切記不能閉門造車,學習的大忌。

5.遇到問題搞不清楚,只能百度,然後自己一團糟

遇到問題的時候,不假思索「百度」,但是很多時候我們是浪費了大量的時間,也搞不清楚自己的問題在哪裡。當然了,我這裏特指“初學者”而不是已經工作中的人。那些已經在做web前端工作的人當然很多問題都要自己解決,但是對於一個web前端新手來說,能找老師盡量找老師,很多問題我們新手不必浪費太多自己的學習時間,因為那樣的效率太慢了。

 二:一些學習的好習慣

1 、與其反覆閱讀,不如經常回顧

大家記住,對於web前端技術性的書籍,絕對不是讓你一頁一頁去看的,像是完成做一樣。有一句話說:溫故而知新,可以為師矣。學過的知識點,你時常去複習一下,你每天都見到你自然就記住了,而不會像很多人說“學了就忘”你不總去回顧,那能不忘記嗎?不如有意識地總結回顧看過的書,學過的知識。只需要每晚趟在床上的時候,回想一下今天都學到了什麼?今天自己有進步一點點嗎?

2 、多做練習,多寫代碼,從錯誤中學習

 一個優秀的web前端開發者,沒有什麼聰明人,他們都是一行一行代碼積累出來的,對於一個初學者來說,如果你想要找到一份不錯的工作,你只能是大量的練習,形成一個好的學習習慣。在初學階段哪怕對着書本敲也沒有什麼問題。認真完成書中留的習題,在自己沒有盡最大努力的情況下面不要去看答案。不要怕犯錯,每一次犯錯都是自己進步的機會。

3 、多總結問題的解決方案,多寫可復用的代碼,拒絕複製粘貼

每天把學習中遇到的問題最後的解決方案總結一下,想想為什麼出現了這個錯誤,加深自己的印象,是什麼導致了這個錯誤,犯過一次的錯誤就盡量不要犯第二次,導致錯誤的根本原因是什麼。是自己的邏輯混亂,粗心大意,還是程序太複雜?

4 、對於自己想要學習的內容,制訂一下計劃,有節奏地學習

一個學習習慣好的人,做什麼事情都會有一個明確的計劃,對於一個web前端初學者來說,一份好的學習計劃是你開始的前提,因為學習一個東西最怕三天打漁,如果能夠持續地學習一個東西,我可以把它學習地很好。這時候,你就應該結合我自身的情況,選擇一段最佳的學習時間,在這段學習時間里我可以不被打擾,保持高度專註。比如每天早上6.30起床看一個小時書。

5、注意勞逸結合

對於電腦工作者來說,多參加體育鍛煉,多去戶外走走,運動能夠增強人的記憶力,並且有時候還能產生靈感。如果身體不好,你的學習效率會非常低,人的精神狀態一旦非常好的時候,做什麼都會如魚得水。想要成為大神,身體好是前提條件。有人30歲成為大神,我資質不好,我35歲成為大神總可以吧。切莫在30歲的時候就把身體弄跨了,然後35歲轉行了,永遠失去了成長為大神的機會。

6.向別人解釋你的知識,多與人討論

一個好的web前端開發者,一定是一個很會思考的,有能力就多寫博客,多分享自己的所學所思,只要你能寫出東西,不怕你寫的太低級,這些對於學習者自身也是非常有益的。通過用別人能夠理解的語言來解釋你學到的東西,本身就要求你對該知識充分理解。另外,很多人經常感嘆「跟你討論一下,我馬上變得有思路了」,這其實就是交流的作用。

前端學習方法很重要,選擇適合自己的學習方法,學好前端最重要的培養持續的興趣,其次就是不斷實踐,從實踐中逐漸練習前端代碼等,從而對前端有很好的理解與吸收。如大家對前端還有不了解的問題,可以持續關注我,每天會發一些關於前端相關知識,供大家學習與參考!

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9894

沐鳴平台_img標籤到底是行內元素還是塊級元素

面試官問你<img>是什麼元素時你怎麼回答

寫這篇文章源自我之前的一次面試,題目便是問img標籤屬於塊級元素還是行內元素,當時想都沒想就說了是行內(inline)元素,面試官追問為什麼能夠設置 <img /> 標籤的寬和高,當時腦子突然一懵,發現這是自己技術上的一個空白,所以有了這篇文章。

閱讀本文您將收穫

<img />標籤的基本使用

MDN關於元素的定義

特殊的可替換元素

<img /> 標籤的基本使用

瀏覽器支持

所有主流瀏覽器都支持 <img> 標籤

標籤定義及使用說明

<img> 標籤定義 html 頁面中的圖像

<img> 標籤有兩個必需的屬性:src 和 alt

強烈推薦在開發中每個圖像中都使用 alt 屬性。這樣即使圖像無法显示,用戶還是可以看到關於丟失了什麼東西的一些信息。而且對於殘疾人來說,alt 屬性通常是他們了解圖像內容的唯一方式

<img />究竟是什麼元素

<img /> 是行內元素還是塊級元素?

<img /> 標籤沒有獨佔一行,所以是行內元素,這沒啥問題

既然是行內元素為什麼能夠設置寬高呢?

這個問題就要引申出下面部分了,<img /> 標籤屬於替換元素,具有內置的寬高屬性,所以可以設置,具體解釋看下面。

元素的定義

從元素本身的特點來講,可以分為不可替換元素和替換元素

元素相關的MDN解釋

不可替換元素

(X)html 的大多數元素是不可替換元素,即其內容直接表現給用戶端(例如瀏覽器)

如:<h1>我是標題</h1>

可替換元素

瀏覽器根據元素的標籤和屬性,來決定元素的具體显示內容

例如瀏覽器會根據 <img> 標籤的src屬性的值來讀取圖片信息並显示出來,而如果查看(X)HTML代碼,則看不到圖片的實際內容;又例如根據 <input> 標籤的type屬性來決定是显示輸入框,還是單選按鈕等

(X)HTML中的 <img>、<input>、<textarea>、<select>、<object> 都是替換元素。這些元素往往沒有實際的內容,即是一個空元素

如:<img src=”tigger.jpg”/>、<input type=”submit” name=”Submit” value=”提交”/>

可替換元素的性質同設置了display:inline-block的元素一致


特殊的可替換元素

<img>屬於可替換元素

<img>同時具有行內元素,行內塊,和塊級元素的特性

替換元素一般有內在尺寸,所以具有 width 和 height,可以設定

例如你不指定 <img> 的 width 和 height 時,就按其內在尺寸显示,也就是圖片被保存的時候的寬度和高度

對於表單元素,瀏覽器也有默認的樣式,包括寬度和高度

<img>、<input>屬於行內替換元素。height/width/padding/margin均可用。效果等於塊元素

來自:https://www.cnblogs.com/programmerzhang/archive/2020/11/27/14047688.html

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9868

沐鳴登錄_Node.js模塊化你所需要知道的事

前言

我們知道,Node.js是基於Commonjs規範進行模塊化管理的,模塊化是面對複雜的業務場景不可或缺的工具,或許你經常使用它,但卻從沒有系統的了解過,所以今天我們來聊一聊Node.js模塊化你所需要知道的一些事兒,一探Node.js模塊化的面貌。

正文

在Node.js中,內置了兩個模塊來進行模塊化管理,這兩個模塊也是兩個我們非常熟悉的關鍵字:require和module。內置意味着我們可以在全局範圍內使用這兩個模塊,而無需像其他模塊一樣,需要先引用再使用。

無需 require('require') or require('module')

在Node.js中引用一個模塊並不是什麼難事兒,很簡單:

const config = require('/path/to/file')

但實際上,這句簡單的代碼執行了一共五個步驟: 

了解這五個步驟有助於我們了解Node.js模塊化的基本原理,也能讓我們甄別一些陷阱,讓我們簡單概括下這五個步驟都做了什麼:

Resolving: 找到待引用的目標模塊,並生成絕對路徑。

Loading: 判斷待引用的模塊內容是什麼類型,它可能是.json文件、.js文件或者.node文件。

Wrapping: 顧名思義,包裝被引用的模塊。通過包裝,讓模塊具有私有作用域。

Evaluating: 被加載的模塊被真正的解析和處理執行。

Caching: 緩存模塊,這讓我們在引入相同模塊時,不用再重複上述步驟。

有些同學看完這五個步驟可能已經心知肚明,對這些原理輕車熟路,有些同學心中可能產生了更多疑惑,無論如何,接下來的內容會詳細解析上述的執行步驟,希望能幫助大家答疑解惑 or 鞏固知識、查缺補漏。

By the way,如果有需要,可以和我一樣,構建一個實驗目錄,跟着Demo進行實驗。

什麼是模塊

想要了解模塊化,需要先直觀地看看模塊是什麼。

我們知道在Node.js中,文件即模塊,剛剛提到了模塊可以是.js、.json或者.node文件,通過引用它們,可以獲取工具函數、變量、配置等等,但是它的具體結構是怎樣呢?在命令行中簡單執行下面的命令就可以看到模塊,也就是module對象的結構:

~/learn-node $ node
> module
Module {
  id: '<repl>',
  exports: {},
  parent: undefined,
  filename: null,
  loaded: false,
  children: [],
  paths: [ ... ] }

可以看到模塊也就是一個普通對象,只不過結構中有幾個特殊的屬性值,需要我們一一去理解,有些屬性,例如id、parent、filename、children甚至都無需解釋,通過字面意思就可以理解。

後續的內容會幫助大家理解這些字段的意義和作用。

Resolving

大致了解了什麼是模塊后,我們從第一個步驟Resolving開始,了解模塊化原理,也就是Node.js如何尋找目標模塊,並生成目標模塊的絕對路徑。

為什麼我們剛剛要先打印module對象,先讓大家了解module的結構呢?因為這裡有兩個字段值id、paths和這個步驟息息相關。一起來看看吧。

首先是id屬性:

每個module都有id屬性,通常這個屬性值是模塊的完整路徑,通過這個值Node.js可以標識和定位模塊的所在位置。但是在這兒並沒有具體的模塊,我們只是在命令行中輸出了module的結構,所以為默認的<repl>值(repl表示交互式解釋器)。

其次是paths屬性:

這個paths屬性有什麼作用呢?Node.js允許我們用多種方式來引用模塊,比如相對路徑、絕對路徑、預置路徑(馬上會解釋),假設我們需要引用一個叫做find-me的模塊,require如何幫助我們找到這個模塊呢?

require('find-me')

我們先打印看看paths中是什麼內容:

~/learn-node $ node
> module.paths
[ '/Users/samer/learn-node/repl/node_modules',
  '/Users/samer/learn-node/node_modules',
  '/Users/samer/node_modules',
  '/Users/node_modules',
  '/node_modules',
  '/Users/samer/.node_modules',
  '/Users/samer/.node_libraries',
  '/usr/local/Cellar/node/7.7.1/lib/node' ]

ok,其實就是一堆系統絕對路徑,這些路徑表示了所有目標模塊可能出現的位置,並且它們是有序的,這意味着Node.js會按序查找paths中列出的所有路徑,如果找到這個模塊,就輸出該模塊的絕對路徑供後續使用。

現在我們知道Node.js會在這一堆目錄中查找module,嘗試執行require(‘find-me’)來查找find-me模塊,由於我們並沒有在任何目錄放置find-me模塊,所以Node.js在遍歷所有目錄之後並不能找到目標模塊,因此報錯Cannot find module ‘find-me’,這個錯誤大家也許經常看到:

~/learn-node $ node
> require('find-me')
Error: Cannot find module 'find-me'
    at Function.Module._resolveFilename (module.js:470:15)
    at Function.Module._load (module.js:418:25)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at repl:1:1
    at ContextifyScript.Script.runInThisContext (vm.js:23:33)
    at REPLServer.defaultEval (repl.js:336:29)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.onLine (repl.js:533:10)

現在,可以嘗試把需要引用的find-me模塊放在上述的任意一個目錄下,在這裏我們創建一個node_modules目錄,並創建find-me.js文件,讓Node.js能夠找到它:

~/learn-node $ mkdir node_modules 

~/learn-node $ echo "console.log('I am not lost');" > node_modules/find-me.js

~/learn-node $ node
> require('find-me');
I am not lost
{}
>

手動創建了find-me.js文件后,Node.js果然找到了目標模塊。當然,當Node.js本地的node_modules目錄中找到了find-me模塊,就不會再去後續的目錄中繼續尋找了。

有Node.js開發經驗的同學會發現在引用模塊時,不一定非得指定到準確的文件,也可以通過引用目錄來完成對目標模塊的引用,例如:

~/learn-node $ mkdir -p node_modules/find-me

~/learn-node $ echo "console.log('Found again.');" > node_modules/find-me/index.js

~/learn-node $ node
> require('find-me');
Found again.
{}
>

find-me目錄下的index.js文件會被自動引入。

當然,這是有規則限制的,Node.js之所以能夠找到find-me目錄下的index.js文件,是因為默認的模塊引入規則是當具體的文件名缺失時尋找index.js文件。我們也可以更改引入規則(通過修改package.json),比如把index -> main:

~/learn-node $ echo "console.log('I rule');" > node_modules/find-me/main.js

~/learn-node $ echo '{ "name": "find-me-folder", "main": "main.js" }' > node_modules/find-me/package.json

~/learn-node $ node
> require('find-me');
I rule
{}
>

require.resolve

如果你只想要在項目中引入某個模塊,而不想立即執行它,可以使用require.resolve方法,它和require方法功能相似,只是並不會執行被引入的模塊方法:

> require.resolve('find-me');
'/Users/samer/learn-node/node_modules/find-me/start.js'
> require.resolve('not-there');
Error: Cannot find module 'not-there'
    at Function.Module._resolveFilename (module.js:470:15)
    at Function.resolve (internal/module.js:27:19)
    at repl:1:9
    at ContextifyScript.Script.runInThisContext (vm.js:23:33)
    at REPLServer.defaultEval (repl.js:336:29)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.onLine (repl.js:533:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:191:7)
>

可以看到,如果該模塊被找到了,Node.js會打印模塊的完整路徑,如果未找到,就報錯。

了解了Node.js是如何尋找模塊之後,來看看Node.js是如何加載模塊的。

模塊間的父子依賴關係

我們把模塊間引用關係,表示為父子依賴關係。

簡單創建一個lib/util.js文件,添加一行console.log語句,標識這是一個被引用的子模塊。

~/learn-node $ mkdir lib
~/learn-node $ echo "console.log('In util');" > lib/util.js

在index.js也輸入一行console.log語句,標識這是一個父模塊,並引用剛剛創建的lib/util.js作為子模塊。

~/learn-node $ echo "require('./lib/util'); console.log('In index, parent', module);" > index.js

執行index.js,看看它們間的依賴關係:

~/learn-node $ node index.js
In util
In index <ref *1> Module {
  id: '.',
  path: '/Users/samer/',
  exports: {},
  parent: null,
  filename: '/Users/samer/index.js',
  loaded: false,
  children: [
    Module {
      id: '/Users/samer/lib/util.js',
      path: '/Users/samer/lib',
      exports: {},
      parent: [Circular *1],
      filename: '/Users/samer/lib/util.js',
      loaded: true,
      children: [],
      paths: [Array]
    }
  ],
  paths: [...]
}

在這裏我們關注與依賴關係相關的兩個屬性:children和parent。

在打印的結果中,children字段包含了被引入的util.js模塊,這表明了util.js是index.js所依賴的子模塊。

但仔細觀察util.js模塊的parent屬性,發現這裏出現了Circular這個值,原因是當我們打印模塊信息時,產生了循環的依賴關係,在子模塊信息中打印父模塊信息,又要在父模塊信息中打印子模塊信息,所以Node.js簡單地將它處理標記為Circular。

為什麼需要了解父子依賴關係呢?因為這關係到Node.js是如何處理循環依賴關係的,後續會詳細描述。

在看循環依賴關係的處理問題之前,我們需要先了解兩個關鍵的概念:exports和module.exports。

exports, module.exports

exports:

exports是一個特殊的對象,它在Node.js中可以無需聲明,作為全局變量直接使用。它實際上是module.exports的引用,通過修改exports可以達到修改module.exports的目的。

exports也是剛剛打印的module結構中的一個屬性值,但是剛剛打印出來的值都是空對象,因為我們並沒有在文件中對它進行操作,現在我們可以嘗試簡單地為它賦值:

// 在lib/util.js的開頭新增一行
exports.id = 'lib/util';

// 在index.js的開頭新增一行
exports.id = 'index';

執行index.js:

~/learn-node $ node index.js
In index Module {
  id: '.',
  exports: { id: 'index' },
  loaded: false,
  ... }
In util Module {
  id: '/Users/samer/learn-node/lib/util.js',
  exports: { id: 'lib/util' },
  parent:
   Module {
     id: '.',
     exports: { id: 'index' },
     loaded: false,
     ... },
  loaded: false,
  ... }

可以看到剛剛添加的兩個id屬性被成功添加到exports對象中。我們也可以添加除id以外的任意屬性,就像操作普通對象一樣,當然也可以把exports變成一個function,例如:

exports = function() {}

module.exports:

module.exports對象其實就是我們最終通過require所得到的東西。我們在編寫一個模塊時,最終給module.exports賦什麼值,其他人引用該模塊時就能得到什麼值。例如,結合剛剛對lib/util的操作:

const util = require('./lib/util');

console.log('UTIL:', util);

// 輸出結果

UTIL: { id: 'lib/util' }

由於我們剛剛通過exports對象為module.exports賦值{id: ‘lib/util’},因此require的結果就相應地發生了變化。

現在我們大致了解了exports和module.exports都是什麼,但是有一個小細節需要注意,那就是Node.js的模塊加載是個同步的過程。

我們回過頭來看看module結構中的loaded屬性,這個屬性標識這個模塊是否被加載完成,通過這個屬性就能簡單驗證Node.js模塊加載的同步性。

當模塊被加載完成后,loaded值應該為true。但到目前為止每次我們打印module時,它的狀態都是false,這其實正是因為在Node.js中,模塊的加載是同步的,當我們還未完成加載的動作(加載的動作包括對module進行標記,包括標記loaded屬性),因此打印出的結果就是默認的loaded: false。

我們用setImmediate來幫助我們驗證這個信息:

// In index.js
setImmediate(() => {
  console.log('The index.js module object is now loaded!', module)
});
The index.js module object is now loaded! Module {
  id: '.',
  exports: [Function],
  parent: null,
  filename: '/Users/samer/learn-node/index.js',
  loaded: true,
  children:
   [ Module {
       id: '/Users/samer/learn-node/lib/util.js',
       exports: [Object],
       parent: [Circular],
       filename: '/Users/samer/learn-node/lib/util.js',
       loaded: true,
       children: [],
       paths: [Object] } ],
  paths:
   [ '/Users/samer/learn-node/node_modules',
     '/Users/samer/node_modules',
     '/Users/node_modules',
     '/node_modules' ] }

ok,由於console.log被後置到加載完成(打完標記)之後,因此現在加載狀態變成了loaded: true。這充分驗證了Node.js模塊加載是一個同步過程。

了解了exports、module.exports以及模塊加載的同步性后,來看看Node.js是如何處理模塊的循環依賴關係。

模塊循環依賴

在上述內容中,我們了解到了模塊之間是存在父子依賴關係的,那如果模塊之間產生了循環的依賴關係,Node.js會怎麼處理呢? 假設有兩個模塊,分別為module1.js和modole2.js,並且它們互相引用了對方,如下:

// lib/module1.js

exports.a = 1;

require('./module2'); // 在這兒引用

exports.b = 2;
exports.c = 3;

// lib/module2.js

const Module1 = require('./module1');
console.log('Module1 is partially loaded here', Module1); // 引用module1並打印它

嘗試運行module1.js,可以看到輸出結果:

~/learn-node $ node lib/module1.js
Module1 is partially loaded here { a: 1 }

結果中只輸出了{a: 1},而{b: 2, c: 3}卻不見了。仔細觀察module1.js,發現我們在module1.js的中間位置添加了對module2.js的引用,也就是exports.b = 2和exports.c = 3還未執行之前的位置。如果我們把這個位置稱作發生循環依賴的位置,那麼我們得到的結果就是在循環依賴發生前被導出的屬性,這也是基於我們上述驗證過的Node.js的模塊加載是同步過程的結論。

Node.js就是這樣簡單地處理循環依賴。在加載模塊的過程中,會逐步構建exports對象,為exports賦值。如果我們在模塊被完全加載前就引用這個模塊,那麼我們只能得到部分的exports對象屬性。

.json和.node

在Node.js中,我們不僅能用require來引用JavaScript文件,還能用於引用JSON或C++插件(.json和.node文件)。我們甚至都不需要顯式地聲明對應的文件後綴。

在命令行中也可以看到require所支持的文件類型:

~ % node
> require.extensions
[Object: null prototype] {
  '.js': [Function (anonymous)],
  '.json': [Function (anonymous)],
  '.node': [Function (anonymous)]
}

當我們用require引用一個模塊,首先Node.js會去匹配是否有.js文件,如果沒有找到,再去匹配.json文件,如果還沒找到,最後再嘗試匹配.node文件。但是通常情況下,為了避免混淆和引用意圖不明,可以遵循在引用.json或.node文件時顯式地指定後綴,引用.js時省略後綴(可選,或都加上後綴)。

.json文件:

引用.json文件很常用,例如一些項目中的靜態配置,使用.json文件來存儲更便於管理,例如:

{
  "host": "localhost",
  "port": 8080
}

引用它或使用它都很簡單:

const { host, port } = require('./config');
console.log(`Server will run at http://${host}:${port}`)

輸出如下:

Server will run at http://localhost:8080

.node文件:

.node文件是由C++文件轉化而來,官網提供了一個簡單的由C++實現的 hello插件 ,它暴露了一個hello()方法,輸出字符串world。有需要的話,可以跳轉鏈接做更多了解並進行實驗。

我們可以通過node-gyp來將.cc文件編譯和構建成.node文件,過程也非常簡單,只需要配置一個binding.gyp文件即可。這裏不詳細闡述,只需要知道生成.node文件后,就可以正常地引用該文件,並使用其中的方法。

例如,將hello()轉化生成addon.node文件后,引用並使用它:

const addon = require('./addon');

console.log(addon.hello());

Wrapping

其實在上述內容中,我們闡述了在Node.js中引用一個模塊的前兩個步驟Resolving和Loading,它們分別解決了模塊的路徑和加載的問題。接下來看看Wrapping都做了什麼。

Wrapping就是包裝,包裝的對象就是所有我們在模塊中寫的代碼。也就是我們引用模塊時,其實經歷了一層『透明』的包裝。

要了解這個包裝過程,首先要理解exports和module.exports之間的區別。

exports是對module.exports的引用,我們可以在模塊中使用exports來導出屬性,但是不能直接替換它。例如:

exports.id = 42; // ok,此時exports指向module.exports,相當於修改了module.exports.

exports = { id: 42 }; // 無用,只是將它指向了{ id: 42 }對象而已,對module.exports不會產生實際改變.

module.exports = { id: 42 }; // ok,直接操作module.exports.

大家也許會有疑惑,為什麼這個exports對象似乎對每個模塊來說都是一個全局對象,但是它又能夠區分導出的對象是來自於哪個模塊,這是怎麼做到的。

在了解包裝(Wrapping)過程之前,來看一個小例子:

// In a.js
var value = 'global'

// In b.js
console.log(value)	// 輸出:global

// In c.js
console.log(value)	// 輸出:global

// In index.html
...
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>

當我們在a.js腳本中定義一個值value,這個值是全局可見的,後續引入的b.js和c.js都是可以訪問該value值。但是在Node.js模塊中卻並不是這樣,在一個模塊中定義的變量具有私有作用域,在其它模塊中無法直接訪問。這個私有作用域如何產生的?

答案很簡單,是因為在編譯模塊之前,Node.js將模塊中的內容包裝在了一個function中,通過函數作用域實現了私有作用域。

通過require(‘module’).wrapper可以打印出wrapper屬性:

~ $ node
> require('module').wrapper
[ '(function (exports, require, module, __filename, __dirname) { ',
  '\n});' ]
>

Node.js不會直接執行文件中的任何代碼,但它會通過這個包裝后的function來執行代碼,這讓我們的每個模塊都有了私有作用域,不會互相影響。

這個包裝函數有五個參數:exports, require, module, __filename, __dirname。我們可以通過arguments參數直接訪問和打印這些參數:

~/learn-node $ echo "console.log(arguments)" > index.js

~/learn-node $ node index.js
{ '0': {},
  '1':
   { [Function: require]
     resolve: [Function: resolve],
     main:
      Module {
        id: '.',
        exports: {},
        parent: null,
        filename: '/Users/samer/index.js',
        loaded: false,
        children: [],
        paths: [Object] },
     extensions: { ... },
     cache: { '/Users/samer/index.js': [Object] } },
  '2':
   Module {
     id: '.',
     exports: {},
     parent: null,
     filename: '/Users/samer/index.js',
     loaded: false,
     children: [],
     paths: [ ... ] },
  '3': '/Users/samer/index.js',
  '4': '/Users/samer' }

簡單了解一下這幾個參數,第一個參數exports初始時為空(未賦值),第二、三個參數require和module是和我們引用的模塊相關的實例,它們倆不是全局的。第四、五個參數__filename和__dirname分別表示了文件路徑和目錄。

整個包裝后的函數所做的事兒約等於:

function (require, module, __filename, __dirname) {
  let exports = module.exports;
  
  // Your Code...
  
  return module.exports;
}

總而言之,wrapping就是將我們的模塊作用域私有化,以module.exports作為返回值將變量或方法暴露出來,以供使用。

Cache

緩存很容易理解,通過一個案例來看看吧:

echo 'console.log(`log something.`)' > index.js
// In node repl
> require('./index.js')
log something.
{}
> require('./index.js')
{}
>

可以看到,兩次引用同一個模塊,只打印了一次信息,這是因為第二次引用時取的是緩存,無需重新加載模塊。

打印require.cache可以看到當前的緩存信息:

> require.cache
[Object: null prototype] {
  '/Users/samer/index.js': Module {
    id: '/Users/samer/index.js',
    path: '/Users/samer/',
    exports: {},
    parent: Module {
      id: '<repl>',
      path: '.',
      exports: {},
      parent: undefined,
      filename: null,
      loaded: false,
      children: [Array],
      paths: [Array]
    },
    filename: '/Users/samer/index.js',
    loaded: true,
    children: [],
    paths: [
      '/Users/samer/learn-node/repl/node_modules',
      '/Users/samer/learn-node/node_modules',
      '/Users/samer/node_modules',
      '/Users/node_modules',
      '/node_modules',
      '/Users/samer/.node_modules',
      '/Users/samer/.node_libraries',
      '/usr/local/Cellar/node/7.7.1/lib/node'
    ]
  }
}

可以看到剛剛引用的index.js文件處於緩存當中,因此不會重新加載模塊。當然我們也可以通過刪除require.cache來清空緩存內容,達到重新加載的目的,這裏不再演示。

總結

本文概述了使用Node.js模塊化時需要了解到的一些基本原理和常識,希望幫助大家對Node.js模塊化有更清晰的認識。但更深入的細節並未在本文中闡述,例如wrapper函數內部的處理邏輯,CommonJS的同步加載的問題、與ES模塊的區別等等。這些未提到的內容大家可以在本文以外做更多探索。

作者:wxbaba

鏈接:https://juejin.cn/post/6899752077807845383

來源:掘金

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9896

沐鳴總代平台_File、Blob、dataURL 和 canvas 的應用與轉換

一、 概念介紹

1. File

(1) 通常情況下, File 對象是來自用戶在一個 input 元素上選擇文件后返回的 FileList 對象,也可以是來自由拖放操作生成的 DataTransfer 對象,或者來自 htmlCanvasElement 上的 mozGetAsFile() API。

(2) File 對象是特殊類型的 Blob,且可以用在任意的 Blob 類型的 context 中。比如:FileReader, URL.createObjectURL(), createImageBitmap(), 及 XMLHttpRequest.send() 都能處理 Blob 和 File。

2. Blob

(1) Blob 對象表示一個不可變、原始數據的類文件對象。它的數據可以按文本或二進制的格式進行讀取,也可以轉換成 ReadableStream 來用於數據操作。

(2) Blob 表示的不一定是JavaScript原生格式的數據。File 接口基於Blob,繼承了 blob 的功能並將其擴展使其支持用戶系統上的文件。

3. dataURL

(1) Data URLs,即前綴為 data: 協議的URL,其允許內容創建者向文檔中嵌入小文件。

(2) Data URLs 由四個部分組成:前綴(data:)、指示數據類型的MIME類型、如果非文本則為可選的base64標記、數據本身:data:[ ][;base64],

4. canvas

(1) Canvas API 提供了一個通過JavaScript 和 html的 canvas 元素來繪製圖形的方式。它可以用於動畫、遊戲畫面、數據可視化、圖片編輯以及實時視頻處理等方面。

二、相互轉化

1. File、Blob 轉化成 dataURL

FileReader 對象允許 Web 應用程序異步讀取文件(或原始數據緩衝區)內容,使用 File 或 Blob 對象指定要讀取的文件或數據。

function fileToDataURL(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
// reader 讀取文件成功的回調
reader.onload = function(e) {
return reader.result
}
}

2. dataURL(base64) 轉化成 Blob(二進制)對象

function dataURLToBlob(fileDataURL) {
let arr = fileDataURL.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while(n --) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], {type: mime})
}

3. File, Blob 文件數據繪製到 canvas

// 思路:File, Blob ——> dataURL ——> canvas

function fileAndBlobToCanvas(fileDataURL) {
let img = new Image()
img.src = fileDataURL
let canvas = document.createElement('canvas')
if(!canvas.getContext) {
alert('瀏覽器不支持canvas')
return;
}
let ctx = canvas.getContext('2d')
document.getElementById('container').appendChild(canvas)
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
}
}

4. 從 canvas 中獲取文件 dataURL

function canvasToDataURL() {
let canvas = document.createElement('canvas')
let canvasDataURL = canvas.toDataURL('image/png', 1.0)
return canvasDataURL
}

本文作者: 卡布奇諾o

本文鏈接:https://www.cnblogs.com/Faith-Yin/p/14036049.html

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9872