天天看點

京東前端高頻面試題彙總

原型修改、重寫

function Person(name) {
    this.name = name
}
// 修改原型
Person.prototype.getName = function() {}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true
// 重寫原型
Person.prototype = {
    getName: function() {}
}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype)        // true
console.log(p.__proto__ === p.constructor.prototype) // false

           

可以看到修改原型的時候p的構造函數不是指向Person了,因為直接給Person的原型對象直接用對象指派時,它的構造函數指向的了根構造函數Object,是以這時候

p.constructor === Object

,而不是

p.constructor === Person

。要想成立,就要用constructor指回來:

Person.prototype = {
    getName: function() {}
}
var p = new Person('hello')
p.constructor = Person
console.log(p.__proto__ === Person.prototype)        // true
console.log(p.__proto__ === p.constructor.prototype) // true


           

什麼是作用域鍊?

首先要了解作用域鍊,當通路一個變量時,編譯器在執行這段代碼時,會首先從目前的作用域中查找是否有這個辨別符,如果沒有找到,就會去父作用域查找,如果父作用域還沒找到繼續向上查找,直到全局作用域為止,,而作用域鍊,就是有目前作用域與上層作用域的一系列變量對象組成,它保證了目前執行的作用域對符合通路權限的變量和函數的有序通路。

await 到底在等啥?

await 在等待什麼呢? 一般來說,都認為 await 是在等待一個 async 函數完成。不過按文法說明,await 等待的是一個表達式,這個表達式的計算結果是 Promise 對象或者其它值(換句話說,就是沒有特殊限定)。

因為 async 函數傳回一個 Promise 對象,是以 await 可以用于等待一個 async 函數的傳回值——這也可以說是 await 在等 async 函數,但要清楚,它等的實際是一個傳回值。注意到 await 不僅僅用于等 Promise 對象,它可以等任意表達式的結果,是以,await 後面實際是可以接普通函數調用或者直接量的。是以下面這個示例完全可以正确運作:

function getSomething() {
    return "something";
}
async function testAsync() {
    return Promise.resolve("hello async");
}
async function test() {
    const v1 = await getSomething();
    const v2 = await testAsync();
    console.log(v1, v2);
}
test();

           

await 表達式的運算結果取決于它等的是什麼。

  • 如果它等到的不是一個 Promise 對象,那 await 表達式的運算結果就是它等到的東西。
  • 如果它等到的是一個 Promise 對象,await 就忙起來了,它會阻塞後面的代碼,等着 Promise 對象 resolve,然後得到 resolve 的值,作為 await 表達式的運算結果。

來看一個例子:

function testAsy(x){
   return new Promise(resolve=>{setTimeout(() => {
       resolve(x);
     }, 3000)
    }
   )
}
async function testAwt(){    
  let result =  await testAsy('hello world');
  console.log(result);    // 3秒鐘之後出現hello world
  console.log('cuger')   // 3秒鐘之後出現cug
}
testAwt();
console.log('cug')  //立即輸出cug

           

這就是 await 必須用在 async 函數中的原因。async 函數調用不會造成阻塞,它内部所有的阻塞都被封裝在一個 Promise 對象中異步執行。await暫停目前async的執行,是以'cug''最先輸出,hello world'和‘cuger’是3秒鐘後同時出現的。

object.assign和擴充運算法是深拷貝還是淺拷貝,兩者差別

擴充運算符:

let outObj = {
  inObj: {a: 1, b: 2}
}
let newObj = {...outObj}
newObj.inObj.a = 2
console.log(outObj) // {inObj: {a: 2, b: 2}}

           

Object.assign():

let outObj = {
  inObj: {a: 1, b: 2}
}
let newObj = Object.assign({}, outObj)
newObj.inObj.a = 2
console.log(outObj) // {inObj: {a: 2, b: 2}}

           

可以看到,兩者都是淺拷貝。

  • Object.assign()方法接收的第一個參數作為目标對象,後面的所有參數作為源對象。然後把所有的源對象合并到目标對象中。它會修改了一個對象,是以會觸發 ES6 setter。
  • 擴充操作符(…)使用它時,數組或對象中的每一個值都會被拷貝到一個新的數組或對象中。它不複制繼承的屬性或類的屬性,但是它會複制ES6的 symbols 屬性。

事件觸發的過程是怎樣的

事件觸發有三個階段:

  • window

    往事件觸發處傳播,遇到注冊的捕獲事件會觸發
  • 傳播到事件觸發處時觸發注冊的事件
  • 從事件觸發處往

    window

    傳播,遇到注冊的冒泡事件會觸發

事件觸發一般來說會按照上面的順序進行,但是也有特例,如果給一個

body

中的子節點同時注冊冒泡和捕獲事件,事件觸發會按照注冊的順序執行。

// 以下會先列印冒泡然後是捕獲
node.addEventListener(
  'click',
  event => {
    console.log('冒泡')
  },
  false
)
node.addEventListener(
  'click',
  event => {
    console.log('捕獲 ')
  },
  true
)

           

通常使用

addEventListener

注冊事件,該函數的第三個參數可以是布爾值,也可以是對象。對于布爾值

useCapture

參數來說,該參數預設值為

false

useCapture

決定了注冊的事件是捕獲事件還是冒泡事件。對于對象參數來說,可以使用以下幾個屬性:

  • capture

    :布爾值,和

    useCapture

    作用一樣
  • once

    :布爾值,值為

    true

    表示該回調隻會調用一次,調用後會移除監聽
  • passive

    :布爾值,表示永遠不會調用

    preventDefault

一般來說,如果隻希望事件隻觸發在目标上,這時候可以使用

stopPropagation

來阻止事件的進一步傳播。通常認為

stopPropagation

是用來阻止事件冒泡的,其實該函數也可以阻止捕獲事件。

stopImmediatePropagation

同樣也能實作阻止事件,但是還能阻止該事件目标執行别的注冊事件。

node.addEventListener(
  'click',
  event => {
    event.stopImmediatePropagation()
    console.log('冒泡')
  },
  false
)
// 點選 node 隻會執行上面的函數,該函數不會執行
node.addEventListener(
  'click',
  event => {
    console.log('捕獲 ')
  },
  true
)

           

其他值到布爾類型的值的轉換規則?

以下這些是假值:

• undefined

• null

• false

• +0、-0 和 NaN

• ""

假值的布爾強制類型轉換結果為 false。從邏輯上說,假值清單以外的都應該是真值。

代碼輸出結果

console.log(1)

setTimeout(() => {
  console.log(2)
})

new Promise(resolve =>  {
  console.log(3)
  resolve(4)
}).then(d => console.log(d))

setTimeout(() => {
  console.log(5)
  new Promise(resolve =>  {
    resolve(6)
  }).then(d => console.log(d))
})

setTimeout(() => {
  console.log(7)
})

console.log(8)

           

輸出結果如下:

1
3
8
4
2
5
6
7

           

代碼執行過程如下:

  1. 首先執行script代碼,列印出1;
  2. 遇到第一個定時器,加入到宏任務隊列;
  3. 遇到Promise,執行代碼,列印出3,遇到resolve,将其加入到微任務隊列;
  4. 遇到第二個定時器,加入到宏任務隊列;
  5. 遇到第三個定時器,加入到宏任務隊列;
  6. 繼續執行script代碼,列印出8,第一輪執行結束;
  7. 執行微任務隊列,列印出第一個Promise的resolve結果:4;
  8. 開始執行宏任務隊列,執行第一個定時器,列印出2;
  9. 此時沒有微任務,繼續執行宏任務中的第二個定時器,首先列印出5,遇到Promise,首選列印出6,遇到resolve,将其加入到微任務隊列;
  10. 執行微任務隊列,列印出6;
  11. 執行宏任務隊列中的最後一個定時器,列印出7。

代碼輸出結果

Promise.resolve().then(() => {
    console.log('1');
    throw 'Error';
}).then(() => {
    console.log('2');
}).catch(() => {
    console.log('3');
    throw 'Error';
}).then(() => {
    console.log('4');
}).catch(() => {
    console.log('5');
}).then(() => {
    console.log('6');
});

           

執行結果如下:

1 
3 
5 
6

           

在這道題目中,我們需要知道,無論是thne還是catch中,隻要throw 抛出了錯誤,就會被catch捕獲,如果沒有throw出錯誤,就被繼續執行後面的then。

元件之間的傳值有幾種方式

1、父傳子
2、子傳父
3、eventbus
4、ref/$refs
5、$parent/$children
6、$attrs/$listeners
7、依賴注入(provide/inject)

           

說一說什麼是跨域,怎麼解決

因為浏覽器出于安全考慮,有同源政策。也就是說,如果協定、域名或者端口有一個不同就是跨域,Ajax 請求會失敗。
為來防止CSRF攻擊
1.JSONP
    JSONP 的原理很簡單,就是利用 <script> 标簽沒有跨域限制的漏洞。    通過 <script> 标簽指向一個需要通路的位址并提供一個回調函數來接收資料當需要通訊時。    <script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
    <script>
        function jsonp(data) {            console.log(data)        }    </script>
    JSONP 使用簡單且相容性不錯,但是隻限于 get 請求。
2.CORS
    CORS 需要浏覽器和後端同時支援。IE 8 和 9 需要通過 XDomainRequest 來實作。
3.document.domain
    該方式隻能用于二級域名相同的情況下,比如 a.test.com 和 b.test.com 适用于該方式。

    隻需要給頁面添加 document.domain = 'test.com' 表示二級域名都相同就可以實作跨域
4.webpack配置proxyTable設定開發環境跨域
5.nginx代理跨域
6.iframe跨域
7.postMessage
    這種方式通常用于擷取嵌入頁面中的第三方頁面資料。一個頁面發送消息,另一個頁面判斷來源并接收消息

           

如何提⾼webpack的打包速度?

(1)優化 Loader

對于 Loader 來說,影響打包效率首當其沖必屬 Babel 了。因為 Babel 會将代碼轉為字元串生成 AST,然後對 AST 繼續進行轉變最後再生成新的代碼,項目越大,轉換代碼越多,效率就越低。當然了,這是可以優化的。

首先我們優化 Loader 的檔案搜尋範圍

module.exports = {
  module: {
    rules: [
      {
        // js 檔案才使用 babel
        test: /\.js$/,
        loader: 'babel-loader',
        // 隻在 src 檔案夾下查找
        include: [resolve('src')],
        // 不會去查找的路徑
        exclude: /node_modules/
      }
    ]
  }
}

           

對于 Babel 來說,希望隻作用在 JS 代碼上的,然後

node_modules

中使用的代碼都是編譯過的,是以完全沒有必要再去處理一遍。

當然這樣做還不夠,還可以将 Babel 編譯過的檔案緩存起來,下次隻需要編譯更改過的代碼檔案即可,這樣可以大幅度加快打包時間

loader: 'babel-loader?cacheDirectory=true'

           

(2)HappyPack

受限于 Node 是單線程運作的,是以 Webpack 在打包的過程中也是單線程的,特别是在執行 Loader 的時候,長時間編譯的任務很多,這樣就會導緻等待的情況。

HappyPack 可以将 Loader 的同步執行轉換為并行的,這樣就能充分利用系統資源來加快打包效率了

module: {
  loaders: [
    {
      test: /\.js$/,
      include: [resolve('src')],
      exclude: /node_modules/,
      // id 後面的内容對應下面
      loader: 'happypack/loader?id=happybabel'
    }
  ]
},
plugins: [
  new HappyPack({
    id: 'happybabel',
    loaders: ['babel-loader?cacheDirectory'],
    // 開啟 4 個線程
    threads: 4
  })
]

           

(3)DllPlugin

DllPlugin 可以将特定的類庫提前打包然後引入。這種方式可以極大的減少打包類庫的次數,隻有當類庫更新版本才有需要重新打包,并且也實作了将公共代碼抽離成單獨檔案的優化方案。DllPlugin的使用方法如下:

// 單獨配置在一個檔案中
// webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    // 想統一打包的類庫
    vendor: ['react']
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].dll.js',
    library: '[name]-[hash]'
  },
  plugins: [
    new webpack.DllPlugin({
      // name 必須和 output.library 一緻
      name: '[name]-[hash]',
      // 該屬性需要與 DllReferencePlugin 中一緻
      context: __dirname,
      path: path.join(__dirname, 'dist', '[name]-manifest.json')
    })
  ]
}

           

然後需要執行這個配置檔案生成依賴檔案,接下來需要使用

DllReferencePlugin

将依賴檔案引入項目中

// webpack.conf.js
module.exports = {
  // ...省略其他配置
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      // manifest 就是之前打包出來的 json 檔案
      manifest: require('./dist/vendor-manifest.json'),
    })
  ]
}

           

(4)代碼壓縮

在 Webpack3 中,一般使用

UglifyJS

來壓縮代碼,但是這個是單線程運作的,為了加快效率,可以使用

webpack-parallel-uglify-plugin

來并行運作

UglifyJS

,進而提高效率。

在 Webpack4 中,不需要以上這些操作了,隻需要将

mode

設定為

production

就可以預設開啟以上功能。代碼壓縮也是我們必做的性能優化方案,當然我們不止可以壓縮 JS 代碼,還可以壓縮 HTML、CSS 代碼,并且在壓縮 JS 代碼的過程中,我們還可以通過配置實作比如删除

console.log

這類代碼的功能。

(5)其他

可以通過一些小的優化點來加快打包速度

  • resolve.extensions

    :用來表明檔案字尾清單,預設查找順序是

    ['.js', '.json']

    ,如果你的導入檔案沒有添加字尾就會按照這個順序查找檔案。我們應該盡可能減少字尾清單長度,然後将出現頻率高的字尾排在前面
  • resolve.alias

    :可以通過别名的方式來映射一個路徑,能讓 Webpack 更快找到路徑
  • module.noParse

    :如果你确定一個檔案下沒有其他依賴,就可以使用該屬性讓 Webpack 不掃描該檔案,這種方式對于大型的類庫很有幫助

懶加載與預加載的差別

這兩種方式都是提高網頁性能的方式,兩者主要差別是一個是提前加載,一個是遲緩甚至不加載。懶加載對伺服器前端有一定的緩解壓力作用,預加載則會增加伺服器前端壓力。

  • 懶加載也叫延遲加載,指的是在長網頁中延遲加載圖檔的時機,當使用者需要通路時,再去加載,這樣可以提高網站的首屏加載速度,提升使用者的體驗,并且可以減少伺服器的壓力。它适用于圖檔很多,頁面很長的電商網站的場景。懶加載的實作原理是,将頁面上的圖檔的 src 屬性設定為空字元串,将圖檔的真實路徑儲存在一個自定義屬性中,當頁面滾動的時候,進行判斷,如果圖檔進入頁面可視區域内,則從自定義屬性中取出真實路徑指派給圖檔的 src 屬性,以此來實作圖檔的延遲加載。
  • 預加載指的是将所需的資源提前請求加載到本地,這樣後面在需要用到時就直接從緩存取資源。 通過預加載能夠減少使用者的等待時間,提高使用者的體驗。我了解的預加載的最常用的方式是使用 js 中的 image 對象,通過為 image 對象來設定 scr 屬性,來實作圖檔的預加載。

為什麼0.1+0.2 ! == 0.3,如何讓其相等

在開發過程中遇到類似這樣的問題:

let n1 = 0.1, n2 = 0.2
console.log(n1 + n2)  // 0.30000000000000004

           

這裡得到的不是想要的結果,要想等于0.3,就要把它進行轉化:

(n1 + n2).toFixed(2) // 注意,toFixed為四舍五入

           

toFixed(num)

方法可把 Number 四舍五入為指定小數位數的數字。那為什麼會出現這樣的結果呢?

計算機是通過二進制的方式存儲資料的,是以計算機計算0.1+0.2的時候,實際上是計算的兩個數的二進制的和。0.1的二進制是

0.0001100110011001100...

(1100循環),0.2的二進制是:

0.00110011001100...

(1100循環),這兩個數的二進制都是無限循環的數。那JavaScript是如何處理無限循環的二進制小數呢?

一般我們認為數字包括整數和小數,但是在 JavaScript 中隻有一種數字類型:Number,它的實作遵循IEEE 754标準,使用64位固定長度來表示,也就是标準的double雙精度浮點數。在二進制科學表示法中,雙精度浮點數的小數部分最多隻能保留52位,再加上前面的1,其實就是保留53位有效數字,剩餘的需要舍去,遵從“0舍1入”的原則。

根據這個原則,0.1和0.2的二進制數相加,再轉化為十進制數就是:

0.30000000000000004

下面看一下雙精度數是如何儲存的:

  • 第一部分(藍色):用來存儲符号位(sign),用來區分正負數,0表示正數,占用1位
  • 第二部分(綠色):用來存儲指數(exponent),占用11位
  • 第三部分(紅色):用來存儲小數(fraction),占用52位

對于0.1,它的二進制為:

0.00011001100110011001100110011001100110011001100110011001 10011...

           

轉為科學計數法(科學計數法的結果就是浮點數):

1.1001100110011001100110011001100110011001100110011001*2^-4

           

可以看出0.1的符号位為0,指數位為-4,小數位為:

1001100110011001100110011001100110011001100110011001

           

那麼問題又來了,指數位是負數,該如何儲存呢?

IEEE标準規定了一個偏移量,對于指數部分,每次都加這個偏移量進行儲存,這樣即使指數是負數,那麼加上這個偏移量也就是正數了。由于JavaScript的數字是雙精度數,這裡就以雙精度數為例,它的指數部分為11位,能表示的範圍就是0~2047,IEEE固定雙精度數的偏移量為1023。

  • 當指數位不全是0也不全是1時(規格化的數值),IEEE規定,階碼計算公式為 e-Bias。 此時e最小值是1,則1-1023= -1022,e最大值是2046,則2046-1023=1023,可以看到,這種情況下取值範圍是

    -1022~1013

  • 當指數位全部是0的時候(非規格化的數值),IEEE規定,階碼的計算公式為1-Bias,即1-1023= -1022。
  • 當指數位全部是1的時候(特殊值),IEEE規定這個浮點數可用來表示3個特殊值,分别是正無窮,負無窮,NaN。 具體的,小數位不為0的時候表示NaN;小數位為0時,當符号位s=0時表示正無窮,s=1時候表示負無窮。

對于上面的0.1的指數位為-4,-4+1023 = 1019 轉化為二進制就是:

1111111011

.

是以,0.1表示為:

0 1111111011 1001100110011001100110011001100110011001100110011001

           

說了這麼多,是時候該最開始的問題了,如何實作0.1+0.2=0.3呢?

對于這個問題,一個直接的解決方法就是設定一個誤差範圍,通常稱為“機器精度”。對JavaScript來說,這個值通常為2-52,在ES6中,提供了

Number.EPSILON

屬性,而它的值就是2-52,隻要判斷

0.1+0.2-0.3

是否小于

Number.EPSILON

,如果小于,就可以判斷為0.1+0.2 ===0.3

function numberepsilon(arg1,arg2){                   
  return Math.abs(arg1 - arg2) < Number.EPSILON;        
}        

console.log(numberepsilon(0.1 + 0.2, 0.3)); // true

           

了解 this 嘛,bind,call,apply 具體指什麼

它們都是函數的方法

call: Array.prototype.call(this, args1, args2])

apply: Array.prototype.apply(this, [args1, args2])

:ES6 之前用來展開數組調用,

foo.appy(null, [])

,ES6 之後使用 ... 操作符

  • New 綁定 > 顯示綁定 > 隐式綁定 > 預設綁定
  • 如果需要使用 bind 的柯裡化和 apply 的數組解構,綁定到 null,盡可能使用 Object.create(null) 建立一個 DMZ 對象

四條規則:

  • 預設綁定,沒有其他修飾(bind、apply、call),在非嚴格模式下定義指向全局對象,在嚴格模式下定義指向 undefined
function foo() {
     console.log(this.a); 
}

var a = 2;
foo();

           
  • 隐式綁定:調用位置是否有上下文對象,或者是否被某個對象擁有或者包含,那麼隐式綁定規則會把函數調用中的 this 綁定到這個上下文對象。而且,對象屬性鍊隻有上一層或者說最後一層在調用位置中起作用
function foo() {
  console.log(this.a);
}

var obj = {
  a: 2,
  foo: foo,
}

obj.foo(); // 2

           
  • 顯示綁定:通過在函數上運作 call 和 apply ,來顯示的綁定 this
function foo() {
  console.log(this.a);
}

var obj = {
  a: 2
};

foo.call(obj);

           

顯示綁定之硬綁定

function foo(something) {
  console.log(this.a, something);

  return this.a + something;
}

function bind(fn, obj) {
  return function() {
    return fn.apply(obj, arguments);
  };
}

var obj = {
  a: 2
}

var bar = bind(foo, obj);

           

New 綁定,new 調用函數會建立一個全新的對象,并将這個對象綁定到函數調用的 this。

  • New 綁定時,如果是 new 一個硬綁定函數,那麼會用 new 建立的對象替換這個硬綁定 this,
function foo(a) {
  this.a = a;
}

var bar = new foo(2);
console.log(bar.a)

           

什麼是margin重疊問題?如何解決?

問題描述: 兩個塊級元素的上外邊距和下外邊距可能會合并(折疊)為一個外邊距,其大小會取其中外邊距值大的那個,這種行為就是外邊距折疊。需要注意的是,浮動的元素和絕對定位這種脫離文檔流的元素的外邊距不會折疊。重疊隻會出現在垂直方向。

計算原則: 折疊合并後外邊距的計算原則如下:

  • 如果兩者都是正數,那麼就去最大者
  • 如果是一正一負,就會正值減去負值的絕對值
  • 兩個都是負值時,用0減去兩個中絕對值大的那個

解決辦法: 對于折疊的情況,主要有兩種:兄弟之間重疊和父子之間重疊 (1)兄弟之間重疊

  • 底部元素變為行内盒子:

    display: inline-block

  • 底部元素設定浮動:

    float

  • 底部元素的position的值為

    absolute/fixed

(2)父子之間重疊

  • 父元素加入:

    overflow: hidden

  • 父元素添加透明邊框:

    border:1px solid transparent

  • 子元素變為行内盒子:

    display: inline-block

  • 子元素加入浮動屬性或定位

浏覽器的主要組成部分

  • ⽤戶界⾯ 包括位址欄、前進/後退按鈕、書簽菜單等。除了浏覽器主窗⼝顯示的您請求的⻚⾯外,其他顯示的各個部分都屬于⽤戶界⾯。
  • 浏覽器引擎 在⽤戶界⾯和呈現引擎之間傳送指令。
  • 呈現引擎 負責顯示請求的内容。如果請求的内容是 HTML,它就負責解析 HTML 和 CSS 内容,并将解析後的内容顯示在螢幕上。
  • ⽹絡 ⽤于⽹絡調⽤,⽐如 HTTP 請求。其接⼝與平台⽆關,并為所有平台提供底層實作。
  • ⽤戶界⾯後端 ⽤于繪制基本的窗⼝⼩部件,⽐如組合框和窗⼝。其公開了與平台⽆關的通⽤接⼝,⽽在底層使⽤作業系統的⽤戶界⾯⽅法。
  • JavaScript 解釋器。⽤于解析和執⾏ JavaScript 代碼。
  • 資料存儲 這是持久層。浏覽器需要在硬碟上儲存各種資料,例如 Cookie。新的 HTML 規範 (HTML5) 定義了“⽹絡資料庫”,這是⼀個完整(但是輕便)的浏覽器内資料庫。

值得注意的是,和⼤多數浏覽器不同,Chrome 浏覽器的每個标簽⻚都分别對應⼀個呈現引擎執行個體。每個标簽⻚都是⼀個獨⽴的程序。

對浏覽器的了解

  • shell 是指浏覽器的外殼:例如菜單,工具欄等。主要是提供給使用者界面操作,參數設定等等。它是調用核心來實作各種功能的。
  • 核心是浏覽器的核心。核心是基于标記語言顯示内容的程式或子產品。

v-model文法糖是怎麼實作的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- v-model 隻是文法糖而已 -->
    <!-- v-model 在内部為不同的輸入元素使用不同的property并抛出不同的事件 -->
    <!-- text和textarea 元素使用value property 和 input事件 -->
    <!-- checkbox 和radio使用checked  property 和 change事件-->
    <!-- select 字段将value 作為prop 并将change 作為事件 -->
    <!-- 注意:對于需要使用輸入法(如中文、日文、韓文等)的語言,你将會發現v-model不會再輸入法    組合文字過程中得到更新 -->
    <!-- 再普通标簽上 -->
    <input v-model="sth" />  //這一行等于下一行
    <input v-bind:value="sth" v-on:input="sth = $event.target.value" />
    <!-- 再元件上 -->
    <currency-input v-model="price"></currentcy-input>
        <!--上行代碼是下行的文法糖         <currency-input :value="price" @input="price = arguments[0]"></currency-input>        --> 
        <!-- 子元件定義 -->
        Vue.component('currency-input', {
         template: `
          <span>
           <input
            ref="input"
            :value="value"
            @input="$emit('input', $event.target.value)"
           >
          </span>
         `,
         props: ['value'],
        })   
</body>
</html>

           

繼續閱讀