Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript问题集锦(二) #40

Open
creeperyang opened this issue Nov 10, 2017 · 5 comments
Open

JavaScript问题集锦(二) #40

creeperyang opened this issue Nov 10, 2017 · 5 comments

Comments

@creeperyang
Copy link
Owner

creeperyang commented Nov 10, 2017

不知不觉,从事前端 4 年多了,距离本篇的前作 JavaScript问题集锦 也有 2 年多了 —— 青葱岁月啊。

又忽然想起“我变秃了,也变强了!”的梗,正好程序员工作久了,容易变秃... 当然我没有秃,所以说 😂

收起半夜突然来的感概,正式解释下文章题目和目的:
本文还是会以 JS 的一个个知识点为粒度来讲,这算是对之前那篇的继承。这个秋天,静极思动,面了BA以及其它一些公司,和面试官的尬聊中,有些觉得很懂的东西并没有解释的很好,这里也提醒下自己和大家:学无止境

1. 从 IIFE 说一说 Expression 和 Statement

IIFE (Immediately Invoked Function Expression) 即立即调用/执行函数表达式。我们常看到(包括某些库中):

(function() {})()

上面的即 IIFE 的一种写法,匿名函数会立即执行。下面是一些等价写法:

(function() {}())
!function() {}()
~function() {}()

是不是觉得很熟悉,然后觉得没什么要注意的?那下面问个问题:

function(){}()

它是 IIFE 吗?为什么?Console 中输入会发生什么?单独拎出来这样问是不是有些发懵?

上面的代码运行的话会报错,并且更进一步,单独执行 function(){} 也会报错:

2017-11-10 11 39 00

下面首先简要解释下原因:

JS 应用是由(无语法错误的) statements 组成的。

当我们单独输入:function (){}时,解释器其实期待的是合法的 statement,即一个函数声明。但很抱歉,函数声明必须有 name,所以这里报错了。

同理,function (){}() 是一样的错误原因,因为当解释器首先看到关键字 function 时,它就认为要接收一个函数声明了,但我们并没有满足这个规则。

下面的图可以帮助理解:

2017-11-11 12 49 17

接下来我们更深入一点,来全面了解下 JS 中的 Statements 和 Expression。

An expression is any valid unit of code that resolves to a value.

对 expression ,一句话:任意合法的产生值的代码单元都是表达式。所以:

  • 5
  • this
  • a = 5
  • func()
  • (function () {})

等等都是表达式。更详细的可参考 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators

对于 statement,我们可以直接看规范 http://es5.github.io/#x12,可以看到 statement 有

  • VariableStatement (变量定义语句)
  • ExpressionStatement (表达式语句)
  • ...

等等。其中,我们重点要讲一讲 ExpressionStatement (表达式语句) ,这是我们这个问题的由来。

ExpressionStatement :
[lookahead ∉ {{, function}] Expression ;

表达式语句的定义如上,用中文解释下就是 合法的 expression 加上 ;(即 expression;)就是表达式语句了。但是,

  • { 开头的不是,因为会和 BlockStatement 产生歧义
  • function 开头的不是,因为会和 FunctionDeclaration 产生歧义

所以你看,一切写在规范里了。

结合规范,我们就知道开头如果是 function,那么一律按函数定义来解析,不合法就报错;而我们可以通过 () 括号/group操作符来避免。

同样{a:1}.a 报错就是因为开头是{被当作 BlockStatement 解释了,想当作对象那加括号吧:({a:1}).a

@cssmagic
Copy link

讲得很清楚,手动点赞! 👍

@creeperyang
Copy link
Owner Author

creeperyang commented Nov 18, 2017

2. 关于 String.fromCharCode 的一点讨论

规范 ,我们可以知道:

String.fromCharCode ( [ char0 [ , char1 [ ,  ] ] ] )

可以接收多个参数,并返回同样多个字符。

比如:

String.fromCharCode(50, 51, 52) // 234

但是,我们可以看一个反例(浏览器未严格遵循规范的):

String.fromCharCode(55297, 56375) // '𐐷'

很有意思对不对?两个参数却只返回一个字符。

当然这是我查阅怎么从 utf16 编码读取字符串时发现的,具体原因也和编码有关,这里先记一下,之后给出详细解答。

https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae 讲得很透彻 ,关心这个知识点的可以自行阅读。下面是一张可能会吸引你去读这篇文章的截图:

2017-11-18 10 59 40

详细解读见 #4

@creeperyang
Copy link
Owner Author

creeperyang commented Feb 21, 2019

3. <script> 标签与asyncdefer 属性

浏览器在解析 HTML 时碰到普通的 <script> 标签,会暂停解析,下载并执行完脚本后,再重新开始解析。我想这个大多数人应该都了解,但asyncdefer 属性有什么区别,大家可能会有疑惑。

image

如上图所示:

1、碰到async(<script async src="app.js"></script>)脚本,浏览器下载脚本的同时继续解析HTML,下载完成后,浏览器暂停解析HTML并执行脚本;

2、碰到defer(<script defer src="app.js"></script>)脚本,浏览器下载脚本的同时继续解析HTML,在HTML解析完成后按序执行脚本;更明确一点,脚本在domInteractive后,在DOMContentLoaded前执行。

3、async不保证各脚本的执行顺序而 defer 保证按序执行。

参考: https://flaviocopes.com/javascript-async-defer/

@creeperyang
Copy link
Owner Author

creeperyang commented Dec 25, 2019

4. <input> 和清除按钮 X 的显示问题

假设这样一个场景,有一个输入框 <input> 与一个清除按钮 X

  • 输入框focus并且有文字时清除按钮显示;(输入框blur时清除按钮隐藏)
  • 点击清除按钮,输入框文字清空。

看起来实现没什么难的,我也不是要问清除按钮怎么用纯CSS来写,问题是,按钮的 click 和输入框的 blur 事件的发生顺序?

blur 先于 click 发生,那么问题来了,blur时清除按钮就被移除,导致 click 事件没触发,也就导致文字不会被清除。

临时方案1 :blur 的回调塞到 setTimeout(fn, 0) 里,让清除按钮的操作延后,是不是就可以让 click 触发?

  • 手机浏览器测试可以;
  • PC 上浏览器测试失败,当setTimeout 到十几毫秒时有概率成功。

不是个可靠方案,放弃。

临时方案2 :不移除按钮DOM,改为设置透明度 0。这样 click 百分百可以触发,但需要考虑透明度 0 点击的时候当作无效点击。

不是完美方案。

方案3 :利用 onmousedownevent.preventDefault() 来让 blurclick 之后发生。

按钮的 onmousedown 优先于输入框的 blur 发生,在 onmousedownpreventDefault 即可避免 blur 发生在 click 之前。

详情见 https://stackoverflow.com/questions/17769005/onclick-and-onblur-ordering-issue

完美。

@creeperyang
Copy link
Owner Author

5. 当出现垂直滚动条之后,为什么出现了水平滚动条?【css、layout、vw】

在测试UI的时候,发现个很有意思的问题:UI一直正常,直到浏览器宽度增加到某个值,出现了水平滚动条。

怀疑是某个元素布局问题,查找,并没找出原因。

用排除法,逐一删除相比线上不同的元素,删除第一个时,UI已经恢复正常,但是反复检查这个元素,并没有什么不对的地方;换第二个删除,UI也恢复正常....

最终找到原因,删除一个元素时,垂直滚动条消失!滚动条的原因!

继续查找原因才知道:宽度单位 vw 是包括滚动条宽度的,即100vw 的宽度是 document.documentElement.offsetWidth,使用 rem 布局时需要额外注意。

html {
    font-size: calc(100vw / 3.75);
}

解决办法:

::-webkit-scrollbar {
    width: 0;
    height: 0;
    display: none;
}

隐藏滚动条即可。

参考:Vertical Scrollbar leads to horizontal scrollbar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants