容問題一直是前端工程師們的夢魘。今天,為大家總結10個容易忽略的前端兼容性問題,供大家參考。
1
在ios8系統中,用h5與APP通信不能傳帶有復雜鏈接符的字符串。
在移動端開發中,經常需要h5與APP進行交互。這時就需要前端和APP開發人員雙方規定一種傳輸協議。在協議中可以添加與APP交互需要的參數。但是在IOS8系統中,不支持參數中有復雜鏈接符,比如JSON格式的字符串、&等等。目前為止,下劃線是唯一支持的連接符。
2
safari中偽元素不支持CSS3動畫。
在項目中肯定有很多前端開發人員使用css的偽元素屬性進行頁面構建。雖然這種方式很方便,但是在safari中并不支持偽元素的CSS3動畫效果。
3
safari中當一個元素的高度為零時,下邊的同級元素的上外邊距會覆蓋這個元素。
在IE、chromet、FF中,即使一個元素的高度為0,也會把它當作塊級元素看待,在頁面中占據相應的位置。但是在safari中,高度為0的元素會被直接忽略。
4
ios系統中在移動瀏覽器的頁面中給按鈕加JS事件,其按鈕必須是原生HTML按鈕或者由<a>標簽自定義構成。
這個問題當時困擾了小編很久,經過一番盤查,終于解決。原來在IOS系統中,瀏覽器只支持給原生HTML按鈕或<a>標簽加JS事件。
5
在移動瀏覽器中給根元素(例如:html)添加overflow:hidden,只有在某些安卓自帶瀏覽器(例如華為的自帶瀏覽器)中才有效。
overflow:hidden這個CSS樣式是大家常用到的。大家用這個樣式可以實現很多目的。其中一個常用的就是隱藏內容溢出,把瀏覽器的滾動條隱藏。這個在PC端瀏覽中毫無問題。但是除了少數安卓自帶瀏覽器,在大多數移動瀏覽器中,給根元素(例如:html)添加這個樣式就會失效。除非給根元素同時添加有實際數值的高度。為了適應移動端頻幕的多種尺寸,只能運用JS動態獲取視窗的高度,然后給根元素設置相同的高度,方可把移動瀏覽器的滾動條隱藏。
6
在某些安卓系統手機自帶瀏覽器(例如:華為手機)中,當父級元素是彈性盒子布局時(含有-webkit-box-flex屬性),其子元素的margin-bottom失效,不能撐開父級元素。
這個問題是小編在某個移動項目開發中碰到的。直接將外邊距(margin)改為內邊距(padding)就可解決。
7
在safair中使用Date.parse()解析時間字符串,其格式必須是YYYY/MM/DD HH:MM:SS。
Date.parse() 方法解析一個表示某個日期的字符串,并返回從1970-1-1 00:00:00 UTC 到該日期對象(該日期對象的UTC時間)的毫秒數,如果該字符串無法識別,或者一些情況下,包含了不合法的日期數值(如:2015-02-31),則返回值為NaN。
上述是JavaScript 參考文檔的說明。嚴格來說,其解析的時間字符串必須是YYYY/MM/DD HH:MM:SS。但是,在IE、chrome、FF中,也可以解析YYYY-MM-DD HH:MM:SS或者YYYY.MM.DD HH:MM:SS這兩種非標準格式的時間字符串。而safari只能解析標準格式。因此,開發人員在使用這個方法時,最好先把非標準格式轉換成標準格式,這樣就可以避免兼容問題。
8
在IOS系統中H5播放器不支持自動播放。
在iphone和ipad上用HTML5播放器時,不能自動播放,apple的解釋說是為用戶節省流量,我覺得這個考慮有點多余。
當時為了解決這個問題,做了些調研,最好的方法就是在IOS系統的瀏覽器中給頁面根元素綁定一次touchstart事件播放流媒體文件,模擬自動播放。
9
標準瀏覽器是只認識documentElement.scrollTop的,但chrome卻不認識這個,在有文檔聲明時,chrome也只認識document.body.scrollTop。
document.body.scrollTop與document.documentElement.scrollTop兩者有個特點,就是同時只會有一個值生效。比如document.body.scrollTop能取到值的時候,document.documentElement.scrollTop就會始終為0;反之亦然。所以,如果要得到網頁的真正的scrollTop值,可以這樣:
任選上述其中一種方式都可以解決。
10
我們常說的事件處理時的event屬性,在標準瀏覽器其是傳入的,IE下由window.event獲取的。并且獲取目標元素的方法也不同,標準瀏覽器是event.target,而IE下是event.srcElement
具體參考《javascript高級程序設計(第3版)》,在此不再贅述。
容問題一直是前端工程師們的夢魘。今天,老K為大家總結10個容易忽略的前端兼容性問題,供大家參考。
1
在ios8系統中,用h5與APP通信不能傳帶有復雜鏈接符的字符串。
在移動端開發中,經常需要h5與APP進行交互。這時就需要前端和APP開發人員雙方規定一種傳輸協議。在協議中可以添加與APP交互需要的參數。但是在IOS8系統中,不支持參數中有復雜鏈接符,比如JSON格式的字符串、&等等。目前為止,下劃線是唯一支持的連接符。
2
safari中偽元素不支持CSS3動畫。
在項目中肯定有很多前端開發人員使用css的偽元素屬性進行頁面構建。雖然這種方式很方便,但是在safari中并不支持偽元素的CSS3動畫效果。
3
safari中當一個元素的高度為零時,下邊的同級元素的上外邊距會覆蓋這個元素。
在IE、chromet、FF中,即使一個元素的高度為0,也會把它當作塊級元素看待,在頁面中占據相應的位置。但是在safari中,高度為0的元素會被直接忽略。
4
ios系統中在移動瀏覽器的頁面中給按鈕加JS事件,其按鈕必須是原生HTML按鈕或者由<a>標簽自定義構成。
這個問題當時困擾了小編很久,經過一番盤查,終于解決。原來在IOS系統中,瀏覽器只支持給原生HTML按鈕或<a>標簽加JS事件。
5
在移動瀏覽器中給根元素(例如:html)添加overflow:hidden,只有在某些安卓自帶瀏覽器(例如華為的自帶瀏覽器)中才有效。
overflow:hidden這個CSS樣式是大家常用到的。大家用這個樣式可以實現很多目的。其中一個常用的就是隱藏內容溢出,把瀏覽器的滾動條隱藏。這個在PC端瀏覽中毫無問題。但是除了少數安卓自帶瀏覽器,在大多數移動瀏覽器中,給根元素(例如:html)添加這個樣式就會失效。除非給根元素同時添加有實際數值的高度。為了適應移動端頻幕的多種尺寸,只能運用JS動態獲取視窗的高度,然后給根元素設置相同的高度,方可把移動瀏覽器的滾動條隱藏。
6
在某些安卓系統手機自帶瀏覽器(例如:華為手機)中,當父級元素是彈性盒子布局時(含有-webkit-box-flex屬性),其子元素的margin-bottom失效,不能撐開父級元素。
這個問題是小編在某個移動項目開發中碰到的。直接將外邊距(margin)改為內邊距(padding)就可解決。
7
在safair中使用Date.parse()解析時間字符串,其格式必須是YYYY/MM/DD HH:MM:SS。
Date.parse() 方法解析一個表示某個日期的字符串,并返回從1970-1-1 00:00:00 UTC 到該日期對象(該日期對象的UTC時間)的毫秒數,如果該字符串無法識別,或者一些情況下,包含了不合法的日期數值(如:2015-02-31),則返回值為NaN。
上述是JavaScript 參考文檔的說明。嚴格來說,其解析的時間字符串必須是YYYY/MM/DD HH:MM:SS。但是,在IE、chrome、FF中,也可以解析YYYY-MM-DD HH:MM:SS或者YYYY.MM.DD HH:MM:SS這兩種非標準格式的時間字符串。而safari只能解析標準格式。因此,開發人員在使用這個方法時,最好先把非標準格式轉換成標準格式,這樣就可以避免兼容問題。
8
在IOS系統中H5播放器不支持自動播放。
在iphone和ipad上用HTML5播放器時,不能自動播放,apple的解釋說是為用戶節省流量,我覺得這個考慮有點多余。
當時為了解決這個問題,做了些調研,最好的方法就是在IOS系統的瀏覽器中給頁面根元素綁定一次touchstart事件播放流媒體文件,模擬自動播放。
9
標準瀏覽器是只認識documentElement.scrollTop的,但chrome卻不認識這個,在有文檔聲明時,chrome也只認識document.body.scrollTop。
document.body.scrollTop與document.documentElement.scrollTop兩者有個特點,就是同時只會有一個值生效。比如document.body.scrollTop能取到值的時候,document.documentElement.scrollTop就會始終為0;反之亦然。所以,如果要得到網頁的真正的scrollTop值,可以這樣:
任選上述其中一種方式都可以解決。
10
我們常說的事件處理時的event屬性,在標準瀏覽器其是傳入的,IE下由window.event獲取的。并且獲取目標元素的方法也不同,標準瀏覽器是event.target,而IE下是event.srcElement
具體參考《javascript高級程序設計(第3版)》,在此不再贅述。
-------------------------------------------------------- END --------------------------------------------------------------------
本文為原創內容,若轉載請注明出處,轉發感激不盡。
者|Austin Tackaberry
譯者|無明
出處丨前端之巔
這篇文章通過簡單的術語和一個真實的例子解釋了 this 是什么以及為什么說它很有用。
我發現,很多教程在解釋 JavaScript 的 this 時,通常會假設你擁有 Java、C++ 或 Python 等面向對象編程語言的背景。這篇文章主要面向那些對 this 沒有先入之見的人。我將嘗試解釋什么是 this 以及為什么它很有用。
或許你遲遲不肯深入探究 this,因為它看起來很奇怪,讓你心生畏懼。你之所以使用它,有可能僅僅是因為 StackOverflow 說你需要在 React 用它來完成一些事情。
在我們深入了解它的真正含義以及為什么要使用它之前,我們首先需要了解函數式編程和面向對象編程之間的區別。
你可能知道也可能不知道,JavaScript 具有函數和面向對象的構造,你可以選擇關注其中一個或兩者兼而有之。
在我的 JavaScript 之旅的早期,我一方面擁抱函數式編程,一方面像避免瘟疫一樣排斥面向對象編程。我對面向對象關鍵字 this 不甚了解。其中的一個原因是我不明白它存在的必要性。在我看來,完全可以不依賴 this 就可以完成所有的事情。
在某種程度上,我的看法是對的。
你可能只關注其中一種范式而從來不去了解另外一種,作為一名 JavaScript 開發者,你的局限性就體現在這里。為了說明函數式編程和面向對象編程之間的差別,我將使用一組 Facebook 好友數據作為示例。
假設你正在構建一個用戶登錄 Facebook 的 Web 應用,在登錄后顯示一些 Facebook 好友的數據。你需要訪問 Facebook 端點來獲取好友的數據,可能包含一些信息,例如 firstName、lastName、username、numFriends、friendData、birthday 和 lastTenPosts。
const data = [ { firstName: 'Bob', lastName: 'Ross', username: 'bob.ross', numFriends: 125, birthday: '2/23/1985', lastTenPosts: ['What a nice day', 'I love Kanye West', ...], }, ... ]
你從(臆造的)Facebook API 獲得上面的數據。現在,你需要轉換它們,讓它們符合項目需要的格式。假設你要為每個用戶的朋友顯示以下內容:
如果使用函數式方法,就是將整個數組或數組的每個元素傳給一個返回所需操作數據的函數:
const fullNames = getFullNames(data) // ['Ross, Bob', 'Smith, Joanna', ...]
你從原始數據開始(來自 Facebook API),為了將它們轉換為對你有用的數據,你將數據傳給一個函數,這個函數將輸出你可以在應用程序中顯示給用戶的數據。
你也可以通過類似的方式獲取三個隨機帖子并計算朋友生日至今的天數。
函數式方法就是指接受原始數據,將數據傳給一個或多個函數,并輸出對你有用的數據。
對于那些剛接觸編程和學習 JavaScript 的人來說,面向對象方法可能會更難掌握。面向對象是指你將每個朋友轉換為對象,對象包含了用于生成你所需內容的一切。
你可以創建包含 fullName 屬性的對象,以及 getThreeRandomPosts 和 getDaysUntilBirthday 函數。
function initializeFriend(data) { return { fullName: `${data.firstName} ${data.lastName}`, getThreeRandomPosts: function() { // get three random posts from data.lastTenPosts }, getDaysUntilBirthday: function() { // use data.birthday to get the num days until birthday } }; } const objectFriends = data.map(initializeFriend) objectFriends[0].getThreeRandomPosts() // Gets three of Bob Ross's posts
面向對象方法是為你的數據創建對象,這些對象包含了狀態和用于生成對你和你的項目有用的數據的信息。
你可能沒有想過會寫出類似 initializeFriend 這樣的東西,你可能會認為它很有用。你可能還會注意到,它其實并非真正的面向對象。
getThreeRandomPosts 或 getDaysUntilBirthday 方法之所以有用,主要是因為閉包。因為使用了閉包,所以在 initializeFriend 返回之后,它們仍然可以訪問 data。
假設你寫了另一個方法,叫 greeting。請注意,在 JavaScript 中,方法只是對象的一個屬性,這個屬性的值是一個函數。我們希望 greeting 可以做這些事情:
function initializeFriend(data) { return { fullName: `${data.firstName} ${data.lastName}`, getThreeRandomPosts: function() { // get three random posts from data.lastTenPosts }, getDaysUntilBirthday: function() { // use data.birthday to get the num days until birthday }, greeting: function() { return `Hello, this is ${fullName}'s data!` } }; }
這樣可以嗎?
不行!
新創建對象的所有東西都可以訪問 initializeFriend 的變量,但對象本身的屬性或方法不行。當然,你可能會問:
難道你不能用 data.firstName 和 data.lastName 來返回 greeting 嗎?
當然可以。但如果我們還想在 greeting 中包含朋友生日至今的天數,該怎么辦?我們必須以某種方式從 greeting 中調用 getDaysUntilBirthday 方法。
是時候讓 this 上場了!
在不同的情況下,this 代表的東西也不一樣。默認情況下,this 指向全局對象(在瀏覽器中,就是 window 對象)。但光知道這點對我們并沒有太大幫助,對我來說有用的是 this 的這條規則:
如果 this 被用在一個對象的方法中,并且這個方法在對象的上下文中調用,那么 this 就指向這個對象本身。
你會問:“在對象的上下文中調用……這又是什么意思”?
別擔心,稍后我們會解釋這個。
因此,如果我們想在 greeting 中調用 getDaysUntilBirthday,可以直接調用 this.getDaysUntilBirthday,因為在這種情況下,this 指向對象本身。
注意:不要在全局作用域或在另一個函數作用域內的常規 ole 函數中使用 this!this 是一個面向對象的構造。因此,它只在對象(或類)的上下文中有意義!
讓我們重構 initializeFriend,讓它使用 this:
function initializeFriend(data) { return { lastTenPosts: data.lastTenPosts, birthday: data.birthday, fullName: `${data.firstName} ${data.lastName}`, getThreeRandomPosts: function() { // get three random posts from this.lastTenPosts }, getDaysUntilBirthday: function() { // use this.birthday to get the num days until birthday }, greeting: function() { const numDays = this.getDaysUntilBirthday() return `Hello, this is ${this.fullName}'s data! It is ${numDays} until ${this.fullName}'s birthday!` } }; }
現在,在執行完 intializeFriend 后,這個對象的所有東西都限定在對象本身。我們的方法不再依賴于閉包,它們將使用對象本身包含的信息。
這是 this 的一種使用方式,現在回到之前的問題:為什么說 this 因上下文不同而不同?
有時候,你希望 this 可以指向不一樣的東西,比如事件處理程序就是一個很好的例子。假設我們想在用戶點擊鏈接時打開朋友的 Facebook 頁面。我們可能會在對象中添加一個 onClick 方法:
function initializeFriend(data) { return { lastTenPosts: data.lastTenPosts, birthday: data.birthday, username: data.username, fullName: `${data.firstName} ${data.lastName}`, getThreeRandomPosts: function() { // get three random posts from this.lastTenPosts }, getDaysUntilBirthday: function() { // use this.birthday to get the num days until birthday }, greeting: function() { const numDays = this.getDaysUntilBirthday() return `Hello, this is ${this.fullName}'s data! It is ${numDays} until ${this.fullName}'s birthday!` }, onFriendClick: function() { window.open(`https://facebook.com/${this.username}`) } }; }
請注意,我們向對象添加了 username,讓 onFriendClick 可以訪問它,這樣我們就可以在新窗口中打開朋友的 Facebook 頁面。現在編寫 HTML:
<button id="Bob_Ross"> <!-- A bunch of info associated with Bob Ross --> </button>
然后是 JavaScript:
const bobRossObj = initializeFriend(data[0]) const bobRossDOMEl = document.getElementById('Bob_Ross') bobRossDOMEl.addEventListener("onclick", bobRossObj.onFriendClick)
在上面的代碼中,我們為 Bob Ross 創建了一個對象。我們獲得與 Bob Ross 相關的 DOM 元素。現在我們想要調用 onFriendClick 方法來打開 Bob 的 Facebook 頁面。應該沒問題吧?
不行!
什么地方出了問題?
請注意,我們為 onclick 處理程序選擇的函數是 bobRossObj.onFriendClick。看到問題所在了嗎?如果我們像這樣重寫它:
bobRossDOMEl.addEventListener("onclick", function() { window.open(`https://facebook.com/${this.username}`) })
現在你看到問題所在了嗎?當我們將 onclick 處理程序指定為 bobRossObj.onFriendClick 時,我們實際上是將 bobRossObj.onFriendClick 的函數作為參數傳給了處理程序。它不再“屬于”bobRossObj,也就是說 this 不再指向 bobRossObj。這個時候 this 實際上指向的是全局對象,所以 this.username 是 undefined 的。
是時候讓 bind 上場了!
我們需要做的是將 this 顯式綁定到 bobRossObj。我們可以使用 bind 來實現:
const bobRossObj = initializeFriend(data[0]) const bobRossDOMEl = document.getElementById('Bob_Ross') bobRossObj.onFriendClick = bobRossObj.onFriendClick.bind(bobRossObj) bobRossDOMEl.addEventListener("onclick", bobRossObj.onFriendClick)
之前,this 是基于默認規則設置的。通過使用 bind,我們在 bobRossObj.onFriendClick 中將 this 的值顯式設置為對象本身,也就是 bobRossObj。
到目前為止,我們已經知道為什么 this 很有用以及為什么有時候需要顯式綁定 this。接下來我們要討論的最后一個主題是箭頭函數。
你可能已經注意到,箭頭函數像是一個時髦的新事物。人們似乎很喜歡它們,因為它們簡潔而優雅。你可能已經知道它們與一般函數略有不同,但不一定非常清楚這些區別究竟是什么。
或許箭頭函數的不同之處在于:
在箭頭函數內部,無論 this 處于什么位置,它指的都是相同的東西。
讓我們用 initializeFriend 示例解釋一下。假設我們想在 greeting 中添加一個輔助函數:
function initializeFriend(data) { return { lastTenPosts: data.lastTenPosts, birthday: data.birthday, username: data.username, fullName: `${data.firstName} ${data.lastName}`, getThreeRandomPosts: function() { // get three random posts from this.lastTenPosts }, getDaysUntilBirthday: function() { // use this.birthday to get the num days until birthday }, greeting: function() { function getLastPost() { return this.lastTenPosts[0] } const lastPost = getLastPost() return `Hello, this is ${this.fullName}'s data! ${this.fullName}'s last post was ${lastPost}.` }, onFriendClick: function() { window.open(`https://facebook.com/${this.username}`) } }; }
這樣可以嗎?如果不行,要怎樣修改才行?
這樣當然是不行的。因為 getLastPost 不是在對象的上下文中調用的,所以 getLastPost 中的 this 會回退到默認規則,即指向全局對象。
“在對象的上下文中調用”可能是一個比較含糊的概念。要確定一個函數是否是在“對象的上下文中”被調用,最好的辦法是看一下函數是如何被調用的,以及是否有對象“附加”在函數上。
讓我們來看看執行 bobRossObj.onFriendClick() 時會發生什么:“找到 bobRossObj 對象的 onFriendClick 屬性,調用分配給這個屬性的函數”。
再讓我們來看看執行 getLastPost() 時會發生什么:”調用一個叫作 getLastPost 的函數”。有沒有注意到,這里并沒有提及任何對象?
現在來測試一下。假設有一個叫作 functionCaller 的函數,它所做的事情就是調用其他函數:
functionCaller(fn) { fn() }
如果我們這樣做會怎樣:functionCaller(bobRossObj.onFriendClick)?可不可以說 onFriendClick 是“在對象的上下文中”被調用的?this.username 的定義存在嗎?
讓我們來看一下:“找到 bobRossObj 對象的 onFriendClick 屬性。找到這個屬性的值(恰好是一個函數),將它傳給 functionCaller,并命名為 fn。現在,執行名為 fn 的函數”。請注意,函數在被調用之前已經從 bobRossObj 對象中“分離”,因此不是“在對象 bobRossObj 的上下文中”調用,所以 this.username 是 undefined 的。
讓箭頭函數來救場:
function initializeFriend(data) { return { lastTenPosts: data.lastTenPosts, birthday: data.birthday, username: data.username, fullName: `${data.firstName} ${data.lastName}`, getThreeRandomPosts: function() { // get three random posts from this.lastTenPosts }, getDaysUntilBirthday: function() { // use this.birthday to get the num days until birthday }, greeting: function() { const getLastPost = () => { return this.lastTenPosts[0] } const lastPost = getLastPost() return `Hello, this is ${this.fullName}'s data! ${this.fullName}'s last post was ${lastPost}.` }, onFriendClick: function() { window.open(`https://facebook.com/${this.username}`) } }; }
箭頭函數是在 greeting 中聲明的。我們知道,當我們在 greeting 中使用 this 時,它指向對象本身。因此,箭頭函數中的 this 指向的對象就是我們想要的。
英文原文:
https://medium.freecodecamp.org/a-deep-dive-into-this-in-javascript-why-its-critical-to-writing-good-code-7dca7eb489e7
*請認真填寫需求信息,我們會在24小時內與您取得聯系。