容问题一直是前端工程师们的梦魇。今天,为大家总结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小时内与您取得联系。