0%

前端细小知识点积累汇总

积硅步以至千里,再完美的知识体系也要由一个个细小的知识点构成

本篇打算积累的东西后边比较多的会在这个地方进行:https://github.com/f2e-awesome/knowledge

框架相关

Flutter——是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。
Next

中间件

redux-saga

编码规范/辅助工具

TypeScript
Prettier
husky

开发工具

Visual Studio Code
Atom
WebStorm

函数式编程、纯函数、高阶组件、柯里化

JavaScript 函数式编程是一个存在了很久的话题,但似乎从 2016 年开始,它变得越来越火热。这可能是因为 ES6 语法对于函数式编程更为友好
我理解的函数式编程:
函数式编程 ( Functional Programming ) 是一种以函数为基础的编程方式和代码组织方式,能够带来更好的代码调试及项目维护的优势。
我认为函数式编程可以理解为,以函数作为主要载体的编程方式,用函数去拆解、抽象一般的表达式
与命令式相比,这样做的好处在哪?主要有以下几点:

  • 语义更加清晰
  • 可复用性更高
  • 可维护性更好
  • 作用域局限,副作用少
    基本的函数式编程
    当情况变得更加复杂时,表达式的写法会遇到几个问题:
  1. 表意不明显,逐渐变得难以维护
  2. 复用性差,会产生更多的代码量
  3. 会产生很多中间变量

链式优化

常见的函数式编程模型
闭包(Closure)
闭包的弊端:
持久化变量不会被正常释放,持续占用内存空间,很容易造成内存浪费,所以一般需要一些额外手动的清理机制。比如根据情况设置数据过期时间什么的
高阶函数:接受或者返回一个函数的函数称为高阶函数——Array.map , Array.reduce, Array.filter
柯里化(Currying):给定一个函数的部分参数,生成一个接受其他参数的新函数
组合(Composing):将多个函数的能力合并,创造一个新的函数
http://taobaofed.org/blog/2017/03/16/javascript-functional-programing/

下边这篇主要介绍实际开发中用到的函数式编程:
http://www.alloyteam.com/2016/09/talk-about-functional-programming/

http://www.ruanyifeng.com/blog/2012/04/functional_programming.html

Http和https的区别,https的数据传输过程是如何的,http2是什么,有什么好处,可以解决现在的什么问题

HTTP/2.0 相比1.0有哪些重大改进
多路复用 (Multiplexing)
多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。
在 HTTP/1.1 协议中 「浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻塞」

二进制分帧
HTTP 性能优化的关键并不在于高带宽,而是低延迟。
HTTP/2 通过让所有数据流共用同一个连接,可以更有效地使用 TCP 连接,让高带宽也能真正的服务于 HTTP 的性能提升。

首部压缩

服务端推送
Server Push 让 HTTP1.x 时代使用内嵌资源的优化手段变得没有意义;如果一个请求是由你的主页发起的,服务器很可能会响应主页内容、logo 以及样式表,因为它知道客户端会用到这些东西。这相当于在一个 HTML 文档内集合了所有的资源,不过与之相比,服务器推送还有一个很大的优势:可以缓存!也让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能。
http://www.alloyteam.com/2016/07/httphttp2-0spdyhttps-reading-this-is-enough/
https://www.zhihu.com/question/34074946

工程化相关:

内存泄漏问题:

概念问题:
https://www.jianshu.com/p/763ba9562864(主要看这个,哪些操作会引起内存泄漏,怎么解决)
具体的排查方法:
http://frontenddev.org/link/js-memory-leak-screening-method-chrome-profiles.html
https://github.com/wengjq/Blog/issues/1

node路径解析规则:

require方法中的文件查找策略:
Node.js的模块分为两类,一类为原生(核心)模块,一类为文件模块。原生模块在Node.js源代码编译的时候编译进了二进制执行文件,加载的速度最快。另一类文件模块是动态加载的,加载速度比原生模块慢。但是Node.js对原生模块和文件模块都进行了缓存,于是在第二次require时,是不会有重复开销的。其中原生模块都被定义在 lib 这个目录下面,文件模块则不定性。

从文件模块缓存中加载

尽管原生模块与文件模块的优先级不同,但是都不会优先于从文件模块的缓存中加载已经存在的模块。

从原生模块加载

原生模块的优先级仅次于文件模块缓存的优先级。require方法在解析文件名之后,优先检查模块是否在原生模块列表中。以http模块为例,尽管在目录下存在一个http/http.js/http.node/http.json文件,require(“http”)都不会从这些文件中加载,而是从原生模块中加载。

原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。

从文件加载

当文件模块缓存中不存在,而且不是原生模块的时候,Node.js会解析require方法传入的参数,并从文件系统中加载实际的文件,加载过程中的包装和编译细节在前一节中已经介绍过,这里我们将详细描述查找文件模块的过程,其中,也有一些细节值得知晓。

require方法接受以下几种参数的传递:

  • http、fs、path等,原生模块。
  • ./mod或../mod,相对路径的文件模块。
  • /pathtomodule/mod,绝对路径的文件模块。
  • mod,非原生模块的文件模块。

简而言之,如果require绝对路径的文件,查找时不会去遍历每一个node_modules目录,其速度最快。其余流程如下:

  1. 从module path数组中取出第一个目录作为查找基准。
  2. 直接从目录中查找该文件,如果存在,则结束查找。如果不存在,则进行下一条查找。
  3. 尝试添加.js、.json、.node后缀后查找,如果存在文件,则结束查找。如果不存在,则进行下一条。
  4. 尝试将require的参数作为一个包来进行查找,读取目录下的package.json文件,取得main参数指定的文件。
  5. 尝试查找该文件,如果存在,则结束查找。如果不存在,则进行第3条查找。
  6. 如果继续失败,则取出module path数组中的下一个目录作为基准查找,循环第1至5个步骤。
  7. 如果继续失败,循环第1至6个步骤,直到module path中的最后一个值。
  8. 如果仍然失败,则抛出异常。

参考:http://www.infoq.com/cn/articles/nodejs-module-mechanism

webpack模块解析规则:

解析相对路径
查找相对当前模块的路径下是否有对应文件或文件夹
是文件则直接加载
是文件夹则继续查找文件夹下的 package.json 文件
有 package.json 文件则按照文件中 main 字段的文件名来查找文件
无 package.json 或者无 main 字段则查找 index.js 文件
解析模块名
查找当前文件目录下,父级目录及以上目录下的 node_modules 文件夹,看是否有对应名称的模块
解析绝对路径(不建议使用)
直接查找对应路径的文件

安全:

参考:http://www.cnblogs.com/-new/p/7135814.html
前言:
共提到了4中攻击方式,分别是xss攻击(关键是脚本,利用恶意脚本发起攻击),CSRF攻击(关键是借助本地cookie进行认证,伪造发送请求),SQL注入(关键是通过用sql语句伪造参数发出攻击),DDOS攻击(关键是通过手段发出大量请求,最后令服务器崩溃)

  • 之所以攻击者能成功攻击,用户操作是一个原因,服务器端没有做好防御是一个问题,因为无法控制用户的操作,所以需要我们服务器端的开发做好防御。没有觉得绝对安全,只要更安全。

这里只说和前端有关系的攻击:

1、XSS:

概念

  • 全称是跨站脚本攻击(Cross Site Scripting),指攻击者在网页中嵌入恶意脚本程序。
    案列

  • 比如说我写了一个博客网站,然后攻击者在上面发布了一个文章,内容是这样的 ,如果我没有对他的内容进行处理,直接存储到数据库,那么下一次当其他用户访问他的这篇文章的时候,服务器从数据库读取后然后响应给客户端,浏览器执行了这段脚本,然后就把该用户的cookie发送到攻击者的服务器了。要知道JS 代码一旦执行,那可就不受控制了,因为它跟网页原有的 JS 有同样的权限。

危害:

  1. 盗取账号。
  2. 控制浏览器行为。

    被攻击的原因
  • 用户输入的数据变成了代码,比如说上面的<script>,应该只是字符串却有了代码的作用。
    预防

  • 将输入的数据进行转义处理,比如说讲 < 转义成&lt;
    除此之外,还可以通过对 cookie 进行较强的控制,比如对敏感的 cookie 增加http-only限制,让 JS 获取不到 cookie 的内容。

引号书写?

2、CSRF:

概念

  • 全称是跨站请求伪造(cross site request forgery),指通过伪装成受信任用户的进行访问,通俗的讲就是说我访问了A网站,然后cookie存在了浏览器,然后我又访问了一个流氓网站,不小心点了流氓网站一个链接(向A发送请求),这个时候流氓网站利用了我的身份对A进行了访问。
    案列

  • 这个例子可能现实中不会存在,但是攻击的方式是一样的。比如说我登录了A银行网站,然后我又访问了室友给的一个流氓网站,然后点了里面的一个链接 www.A.com/transfer?account=666&money=10000,那么这个时候很可能我就向账号为666的人转了1w软妹币

  • 注意这个攻击方式不一定是我点了这个链接,也可以是这个网站里面一些资源请求指向了这个转账链接,比如说一个
    被攻击的原因

  • 用户本地存储cookie,攻击者利用用户的cookie进行认证,然后伪造用户发出请求

  • 某些页面的删除请求(拼接的json接口)在别的网站的中发起,不带token很容易就可以通过了,带token的可以防止,是因为其他网站跨域很难获取到token,即便token写在页面中。token放在页面中、相应头中、response中都可以只要前端能获取到在请求的时候放在request 头中就可以了
    预防

  • 之所以被攻击是因为攻击者利用了存储在浏览器用于用户认证的cookie,那么如果我们不用cookie来验证不就可以预防了。所以我们可以采用csrf token(不存储于浏览器)认证。

  • 通过referer识别,HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理。那么这样的话,我们必须登录银行A网站才能进行转账了。

    浏览器中输入任意路径回车是没有referer的(不论是html,还是json请求),输入访问链接后network html之外其他的加载都有,已经加载出来的页面中进行请求操作也有referer
    

关于CSRF Tokens
csrf 是在获取不到cookie的情况下才使用的,利用用户权限做的操作。
页面刷新,服务器生成token到页面,服务器自己也备份一份,表单请求的时候验证是否相等,不相等视为csrf攻击。
此token可以设置实效时间防治暴力破解什么的。页面刷新时token更新。
第三方是很难获取到token的
referer也可以一定程度上有效果,但是好像比较麻烦好像可以被第三方重制
另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行 CSRF 的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF 攻击,需要保护。而查询余额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。
https://www.imooc.com/article/18069
https://zhuanlan.zhihu.com/p/22521378
https://blog.csdn.net/lion19930924/article/details/50955000

扩展:

身份验证token
基于 Token 的身份验证方法

使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

Cookie:
首先必须明确一点,存储cookie是浏览器提供的功能。cookie 其实是存储在浏览器中的纯文本,浏览器的安装目录下会专门有一个 cookie 文件夹来存放各个域下设置的cookie。

当网页要发http请求时,浏览器会先检查是否有相应的cookie,有则自动添加在request header中的cookie字段中。这些是浏览器自动帮我们做的,而且每一次http请求浏览器都会自动帮我们做。如果数据并不是每个请求都需要发给服务端的数据,浏览器这设置自动处理无疑增加了网络开销。

但在 localStorage 出现之前,cookie被滥用当做了存储工具。什么数据都放在cookie中,即使这些数据只在页面中使用而不需要随请求传送到服务端。当然cookie标准还是做了一些限制的:每个域名下的cookie 的大小最大为4KB,每个域名下的cookie数量最多为20个(但很多浏览器厂商在具体实现时支持大于20个)。

httpOnly
当cookie带httpOnly选项时,客户端则无法通过js代码去访问(包括【读取】、【修改】、【删除】等)这个cookie。
这种类型的cookie只能通过服务端来设置。

服务端设置 cookie

  • 一个set-Cookie字段只能设置一个cookie,当你要想设置多个 cookie,需要添加同样多的set-Cookie字段
  • 服务端可以设置cookie 的 所有选项:expires、domain、path、secure、HttpOnly
  • 客户端可以设置cookie 的下列选项:expires、domain、path、secure(有条件:只有在https协议的网页中,客户端设置secure类型的 cookie 才能成功),但无法设置HttpOnly选项。

用 js 如何设置多个 cookie

当要设置多个cookie时, js 代码很自然地我们会这么写:

document.cookie = “name=Jonh; age=12; class=111”;
但你会发现这样写只是添加了第一个cookie“name=John”,后面的所有cookie都没有添加成功。所以最简单的设置多个cookie的方法就在重复执行document.cookie = “key=name”,如下:

document.cookie = “name=Jonh”;
document.cookie = “age=12”;
document.cookie = “class=111”;

补充:

  1. 什么时候 cookie 会被覆盖:name/domain/path 这3个字段都相同的时候;
  2. 关于domain的补充说明(参考1/参考2):
    1. 如果显式设置了 domain,则设置成什么,浏览器就存成什么;但如果没有显式设置,则浏览器会自动取 url 的 host 作为 domain 值;
    2. 新的规范中,显式设置 domain 时,如果 value 最前面带点,则浏览器处理时会将这个点去掉,所以最后浏览器存的就是没有点的(注意:但目前大多数浏览器并未全部这么实现)
    3. 前面带点‘.’和不带点‘.’有啥区别:
      • 带点:任何 subdomain 都可以访问,包括父 domain
      • 不带点:只有完全一样的域名才能访问,subdomain 不能(但在 IE 下比较特殊,它支持 subdomain 访问)

简单明了区分escape、encodeURI和encodeURIComponent
1、如果只是编码字符串,不和URL有半毛钱关系,那么用escape。
2、如果你需要编码整个URL,然后需要使用这个URL,那么用encodeURI。
3、当你需要编码URL中的参数的时候,那么encodeURIComponent是最好方法。

移动端相关

响应式页面开发:

步骤 1 - 添加 viewport meta 标签

步骤 2 - 使用 Media Queries
2 种用法
<link rel=”stylesheet” media=”(max-width: 640px)” href=”max-640px.css”>
@media (max-width: 640px) { /当视窗宽度小于或等于 640px 时,这里的样式将生效/}
样式断点:可以从业界一些热门可靠的 CSS 框架中寻找参考答案,例如 Bulma,其采用的「样式断点」有 5 个,实际工作中需要我们同视觉设计师一起沟通确认,因为视觉设计师可能需要根据不同的断点为页面设计不同的视觉表现。

步骤 3 - 使用 Viewport 单位及 rem
Media Queries 只解决了「为不同特性的浏览器视窗使用不同的样式代码」的问题,而 Viewport 单位及 rem 的应用,则是为了解决第二个问题:让页面元素的尺寸能够依据浏览器视窗尺寸变化而平滑变化。

方法 1 - 仅使用 vw 作为 CSS 长度单位

  1. 利用 Sass 函数将设计稿元素尺寸的像素单位转换为 vw 单位
    // iPhone 6尺寸作为设计稿基准
    $vw_base: 375;
    @function vw($px) {
    @return ($px / $vm_base) * 100vw;
    }

  2. 无论是文本字号大小还是布局高宽、间距、留白等都使用 vw 作为 CSS 单位
    .mod_nav {
    background-color: #fff;
    &_list {
    display: flex;
    padding: vw(15) vw(10) vw(10); // 内间距
    &_item {
    flex: 1;
    text-align: center;
    font-size: vw(10); // 字体大小
    &_logo {
    display: block;
    margin: 0 auto;
    width: vw(40); // 宽度
    height: vw(40); // 高度
    img {
    display: block;
    margin: 0 auto;
    max-width: 100%;
    }
    }
    &_name {
    margin-top: vw(2);
    }
    }
    }
    }

  3. 1 物理像素线(也就是普通屏幕下 1px ,高清屏幕下 0.5px 的情况)采用 transform 属性 scale 实现
    .mod_grid {
    position: relative;
    &::after {
    // 实现1物理像素的下边框线
    content: ‘’;
    position: absolute;
    z-index: 1;
    pointer-events: none;
    background-color: #ddd;
    height: 1px;
    left: 0;
    right: 0;
    top: 0;
    @media only screen and (-webkit-min-device-pixel-ratio: 2) {
    -webkit-transform: scaleY(0.5);
    -webkit-transform-origin: 50% 0%;
    }
    }

    }

  4. 对于需要保持高宽比的图,应改用 padding-top 实现
    .mod_banner {
    position: relative;
    // 使用padding-top 实现宽高比为 100:750 的图片区域
    padding-top: percentage(100/750);
    height: 0;
    overflow: hidden;
    img {
    width: 100%;
    height: auto;
    position: absolute;
    left: 0;
    top: 0;
    }
    }

由此,我们不需要增加其他任何额外的脚本代码就能够轻易实现一个常见布局的响应式页面,效果如下:

方法 2 - vw 搭配 rem,寻找最优解,其实vw方案也可以限制宽度
方法1依赖于视窗大小而自动缩放,无论视窗过大还是过小,它也随着视窗过大或者过小,失去了最大最小宽度的限制,有时候不一定是我们所期待的展示效果。
当然,你可以不在乎移动端页面在 PC 上的展现效果,但如果有低成本却有效的办法来修复这样的小瑕疵,是真切可以为部分用户提升体验的。

通过以下步骤来进行优化:

  1. 给根元素的字体大小设置随着视窗变化而变化的 vw 单位,这样就可以实现动态改变其大小
  2. 其他元素的文本字号大小、布局高宽、间距、留白都使用 rem 单位
  3. 限制根元素字体大小的最大最小值,配合 body 加上最大宽度和最小宽度,实现布局宽度的最大最小限制
    核心代码实现如下:
    // rem 单位换算:html的font-size定为 75px(即1rem) 只是方便运算,750px-75px、640-64px、1080px-108px,如此类推
    $vw_fontsize: 75; // iPhone 6尺寸的根元素大小基准值
    @function rem($px) {
    @return ($px / $vw_fontsize ) * 1rem;
    }
    // 根元素大小使用 vw 单位
    $vw_design: 750;
    html {
    font-size: ($vw_fontsize / ($vw_design / 2)) * 100vw;
    // 同时,通过Media Queries 限制根元素最大最小值
    @media screen and (max-width: 320px) {
    font-size: 64px;
    }
    @media screen and (min-width: 540px) {
    font-size: 108px;
    }
    }
    // body 也增加最大最小宽度限制,避免默认100%宽度的 block 元素跟随 body 而过大过小
    body {
    max-width: 540px;
    min-width: 320px;
    }

扩展阅读:
http://imweb.io/topic/5a523cc0a192c3b460fce3a5
https://segmentfault.com/a/1190000008767416

https://www.jianshu.com/p/04efb4a1d2f8
http://www.html-js.com/article/Mobile-terminal-H5-mobile-terminal-HD-multi-screen-adaptation-scheme%203041
总结:
flexible.js 方案(参考上边后两篇)
——flexible.js,在html头部引入,用来获取dpr和定义不同设备上1rem是多少px , 同时会设置一些scale解决1px,用js进行px-rem转换等问题
设计稿是用来计算元素的各种尺寸为多少rem的(比如750px宽的设计稿,定义75px位1rem——比例和flexible一样,然后以此计算rem值)

vw布局方案同flexible.js需要在css中计算比例(px单位的长度是多少rem, 或者是多少vw),不同的是vw方案不用指定不同终端中1rem是多少px,vm是css天然支持的单位,不同终端会按照总宽度为100vw的设定按比例分配。

滑屏应用:

手势动作判断是实现滑屏应用的核心逻辑。 对于上下滑屏应用,我们主要实现的手势动作有:瞬间的上下滑动和按住拖拽滑动。
瞬间的上下滑动除了要考虑滑动的始末位置,还要考虑时间间隔,即滑动速度。若满足一定的速度则代表用户是果断切换上下屏的动作,反之,则是犹豫保留在当前屏的动作。
var _this = this
var drag = false
var y0 = 0
var deltaY = 0
var time0 = 0

this.$swiper.on(‘touchstart’, function (ev) {
drag = _this.$swiper
y0 = ev.touches[0].pageY
time0 = new Date()
})

this.$swiper.on(‘touchend’, function (ev) {
var interval = new Date() - time0
drag = false
// 拖拽完成后,判断移动方向、移动速度和移动距离等
// 若划动速度满足,则执行上划或下划过渡动画。
// 若划动速度不满足,则判断是否划动距离是否大于阈值(如 Swiper 容器的高度的一半),若大于则执行上划或下划过渡画面,反之回到当前活跃块。
_this.panEnd(deltaY, deltaY / interval)
})

this.$swiper.on(‘touchmove’, function (ev) {
if (drag) {
deltaY = ev.touches[0].pageY - y0
// 设置 .swiper-wrapper 按住拖拽的位移。
_this.pan(deltaY)
}
})
事实上,业界已经有许多很好用的判断手势动作的插件,如知名的 hammer.js 或 zepto 的 touch 模块。

移动端知识点之前的总结:

布局视口:所以,在手机上,视口与移动端浏览器屏幕宽度不再相关联,是完全独立的,这个浏览器厂商定的视口被称为布局视口。
视觉视口:视觉视口是用户正在看到的网页的区域,大小是屏幕中CSS像素的数量。
理想视口:布局视口明显对用户是不友好的,完全忽略了手机本身的尺寸。理想视口是网页用户最理想的宽度,用户进入页面的时候不需要缩放。
解决各种浏览器兼容问题的理想视口设置:
设备像素比:
缩放程度为100%时,设备像素比:dpr = 设备像素(屏幕物理像素,单位pt) / CSS像素(单位px) —— 可以通过JS得到: window.devicePixelRatio
即:dpr = 屏幕横向设备像素 / 理想视口的宽

移动端的适配布局方案:
Flexible的布局方案(根本是rem)
vw(viewport单位)来做移动端的适配问题。

Flexible承载的使命:

  • 根据dpr的值来修改viewport实现1px的线
  • 根据dpr的值来修改html的font-size,从而使用rem实现等比缩放(?好像是达到字体大小不变的效果吧)
  • 使用Hack手段用rem模拟vw特性(css hack — 浏览器兼容 的意思)
    vw:
  • 各终端下的适配问题 — 不同的终端,我们面对的屏幕分辨率、DPR、1px、2x图等一系列的问题。只不过解决这些问题不再是使用Hack手段来处理,而是直接使用原生的CSS技术来处理的。
  • Retina屏的细节处理
    100vw = 750px,即1vw = 7.5px

参考链接:
https://github.com/riskers/blog/issues/17 (基础概念)
https://www.w3cplus.com/css/vw-for-layout.html(Flexible方案和vw方案综述)
http://www.html-js.com/article/Mobile-terminal-H5-mobile-terminal-HD-multi-screen-adaptation-scheme%203041(Flexible方案)
https://www.w3cplus.com/mobile/vw-layout-in-vue.html(vw方案)

Js相关:

new 操作发生了什么
当代码 new Foo(…) 执行时,会发生以下事情:

  1. 一个继承自 Foo.prototype 的新对象被创建。
  2. 将 this 绑定到新创建的对象,执行构造函数中的代码(为新对象添加属性)
  3. 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

跟以下代码效果差不多:
function instantiate(fn, …rests) {
var f = Object.create(fn.prototype);
var val = fn.apply(f, rests);
return isPrimitive(val) ? f : val;
}
function A() {}
a = instantiate(A);

Ps: isPrimitive() 太麻烦了,不是问题的核心,在此就不实现了
参考: https://www.zhihu.com/question/36440948

router的实现:
https://github.com/kaola-fed/blog/issues/137

获取页面元素位置和宽高?

宽高:
clientHeight和clientWidth content + padding
clientTop和clientLeft 左,上边框宽度 border
offsetHeight和offsetWidth content + padding + border
offsetTop和offsetLeft. 元素的左上角(边框外边缘)与已定位的父容器(offsetParent对象)左上角的距离
offsetParent offsetParent指的是距该元素最近的position不为static的祖先元素,如果没有则指向body元素。

位置:
//取得元素x坐标
function pageX(elem) {
return elem.offsetParent?(elem.offsetLeft+pageX(elem.offsetParent)):elem.offsetLeft;
}
//取得元素y坐标
function pageY(elem) {
return elem.offsetParent?(elem.offsetTop+pageY(elem.offsetParent)):elem.offsetTop;
}

||

getBoundingClientRect()方法。它返回一个对象,其中包含了left、right、top、bottom四个属性

  var X= this.getBoundingClientRect().left;

  var Y =this.getBoundingClientRect().top;
再加上滚动距离,就可以得到绝对位置

  var X= this.getBoundingClientRect().left+document.documentElement.scrollLeft;

  var Y =this.getBoundingClientRect().top+document.documentElement.scrollTop;