其实我们现在已经不是必须使用Generator来实现异步代码编写成同步代码的结构了function fetchData(url){ return new Promist((resolve)=>{ setTimeout(()=>{ resolve(`${url} data~`) },2000); }) } // 定义函数：异步函数 async function getData(){ const res1 = await fetchData("aaa") console.log(res1) const res2 = await fetchData(res1 + "bbb") console.log(res2) const res3 = await fetchData(res2+"ccc") console.log(res3) } getData()感谢观看
其实并不存在正真意义上的CSS3定义，因为CSS3并不是一个单一的规范，而是一系列独立模块的组合，这些模块扩展了CSS3的功能。这种模块模块化的方法允许不同的特性以不同的速度发展，可以更快的标准化一些特性，而不必等待整个规范的形成。这里我们可以看下面这几个模块：选择器 Selectors在css3中的新的特性比如[attr^=value]属性值以特定的字符串开始，当然还新增了结构性伪类，比如:nth-child、:nth-last-child、:first-of-type背景和边框 Backgrounds and Borders新增了边框图片可以使用图片来定义盒子的边框border-image。多重背景可以支持在单个元素上使用多个背景图片。文本效果 Text Effets文本阴影，可以将文本后面添加阴影的效果text-shadow。还有文本溢出属性text-overflow可以控制文本溢出容器之后的显示方式。转换和动画2D和3D之间的转换：我们可以使用transform,包括螺旋rotate，缩放scale，倾斜skew，平移translate。CSS动画animation运行定义关键帧动画，控制动画序列。感谢大家观看
标签的defer属性和async属性用来控制外部文件的加载和执行方式。它对于改善网页的加载速度有很大帮助。但是他们的机制不同，defer属性不会阻塞DOM树的渲染，并且defer属性的执行是在DOM树渲染之后DOMContentLoaded事件之前执行，还有就是多个defer属性执行是按照代码顺序执行的。async属性在执行的时候也不会阻塞DOM树的构建，但是它不会保证是不是在DOMContentLoaded之前或者之后执行，并且多个async属性的标签不按照顺序依次执行，因为它是独立的哪一个先加载完哪一个先运行。运行场景由于以上特性造成了他们的使用场景不同，defer属性通常是用在DOM树构建完成操作DOM的场景，并且对script脚本的运行顺序有限制的。然而async属性通常使用在对网站检控或者广告类型的这些对DOM树构建不需要依赖的，独立运行的js脚本。现代化框架开发解决方案在现代化框架开发过程中，往往不需要我们自己来配置async或者defer:在使用脚手架或者自己搭建的webapck或者vite项目进行打包时，它会根据需要帮我们加上defer属性。某些情况下我们想要进行性能优化时，也可以手动的加上async属性,例如一些第三方的分析工具或者广告追踪脚本。感谢大家观看
async属性和defer属性目标一样它也是为了不让js阻塞DOM树的构建。不过他们两个还是有区别的。async让js脚本的下载和执行是独立的。浏览器不会因为async属性的script脚本的执行而阻塞，这一点和defer属性类似。然而async属性比较任性，只要脚本被浏览器下载完成之后就会立即执行，不会等待在DOMContentLoaded之前执行。所以它不能保证是在DOMContentLoaded事件之前或者之后执行。并且它在执行的时候会阻塞DOM树的构建。代码如下图所示：下面我们看一下，async属性和defer属性的区别，下方的图片很详细。[那我们这个async属性有什么用呢？感觉有defer属性就够了，其实当我们引用那些统计网站数据的第三方脚本的时候就可以使用async属性。它的执行不会影响当前站点的DOM树渲染，就比如百度收录的验证代码或者bing的还有统计网站的其他代码或广告代码。感谢大家观看
我们知道，当浏览器在执行到script标签的时候，首先会停止构建DOM树，然后下载Javascript文件并且执行，当JavaScript脚本执行完毕之后才会继续解析HTML标签构建DOM树。为什么Javascript程序会这样做呢？原因是我们的Javascript的作用就是操作DOM并且可以修改DOM。如果我们等到HTML执行完成之后再去执行JavaScript就会造成严重的回流和重绘，尤其是现代化编程中大部分用的都是Vue和React框架。页面都是由Javascript来渲染页面的，十分影响网页性能。所以在渲染DOM树的情况下，当遇到Javascript都会先下载然后执行，再渲染DOM树。为了解决这个问题，JavaScript语言的script标签给了我们两个属性：defer属性和async属性。defer的作用是什么？defer属性告诉我们，让页面不要等待JavaScript语言下载，而是继续执行HTML构建DOM树。脚本会由浏览器来执行下载，这个过程不会阻塞构建DOM树。如果脚本下载好了，他会等待DOM树构建好了，在DOMContentLoaded事件执行之前执行JavaScript程序defer中的代码。如下图：所以反过来说，DOMContentLoaded事件总会等待defer中的代码执行完毕之后再运行。还有值得一提的是当页面中有多个script标签中有defer属性的时候，它会根据标签的先后顺序依次进行。所以defer属性推荐放到script标签中，并且最好放在head标签中。为什么最好放在head标签中呢？因为DOM树构建的过程中，会按照标签的先后顺序执行，假如我们将带defer属性的script标签放到最后。其实不会有多大效果，从内存的角度来说就是带defer属性的script标签相当于给我们的代码新增了一个线程来执行代码。当然不影响DOM树的构建。这里我们考虑到了下载JavaScript代码也是需要时间的。不如放在head标签中增加效率。感谢大家观看
网页的解析过程我们的浏览器在一个网页解析的过程中，如何执行呢？其实一个网页在执行中，先下载我们的html基本框架，当遇到link标签加载.css文件的时候会下载，下载了css会生成CSSOM树，这里的加载不是阻塞下载，然而当遇到.js文件的时候就去下载.js文件，下载js文件的时候是阻塞下载。然后，将CSSOM树挂载到html上，然后将下载出来的js文件再用于html上，再执行渲染，从而生成真实DOM。注意在这里渲染的时候会先做layout布局，判断文档样式解构，并且查看是否display:none,有就不显示了，并且绘制图像。这里我们就不得不讲讲浏览器内核了。浏览器内核常见的浏览器内核：1.Trident（三叉戟）：IE、360安全浏览器、搜狗高速浏览器、百度浏览器、UC浏览器；Gecko（壁虎）：Mozilla Firefox；2.Presto（急板乐曲）->Blink（眨眼）：Opera3.Webkit：Safari、360极速浏览器、搜狗高速浏览器、移动端浏览器（Android、iOS）4.Webkit->Blink：Google Chrome，Edge我们最常用的谷歌浏览器v8引擎是Blink内核的。我们经常说的浏览器内核指的是浏览器的排版引擎：排版引擎（layout engine），也称为浏览器引擎（browser engine）、页面渲染引擎（rendering engine）或样版引擎浏览器渲染流程渲染引擎如何解析页面我们先看下面这张图更详细的解析流程：https://www.html5rocks.com/en/tutorials/internals/howbrowserswork解析一：html的解析过程1.因为默认情况下服务器会给浏览器返回index.html文件，所以解析HTML是所有步骤的开始：2.解析HTML，会构建DOM Tree解析二：生成CSS规则在解析的过程中，如果遇到CSS的link元素，那么会由浏览器负责下载对应的CSS文件： 注意：下载CSS文件是不会影响DOM的解析的；浏览器下载完CSS文件后，就会对CSS文件进行解析，解析出对应的规则树：我们可以称之为CSSOM（CSS Object Model，CSS对象模型）；解析三–构建Render Tree当有了DOM Tree和CSSOM Tree后，就可以两个结合来构建Render Tree了注意一：link元素不会阻塞DOM Tree的构建过程，但是会阻塞Render Tree的构建过程这是因为Render Tree在构建时，需要对应的CSSOM Tree；注意二：Render Tree和DOM Tree并不是一一对应的关系，比如对于display为none的元素，压根不会出现在render tree中；解析四–布局（layout）和绘制（Paint）第四步是在渲染树（Render Tree）上运行布局（Layout）以计算每个节点的几何体。渲染树会表示显示哪些节点以及其他样式，但是不表示每个节点的尺寸、位置等信息；布局是确定呈现树中所有节点的宽度、高度和位置信息；第五步是将每个节点绘制（Paint）到屏幕上在绘制阶段，浏览器将布局阶段计算的每个frame转为屏幕上实际的像素点；包括将元素的可见部分进行绘制，比如文本、颜色、边框、阴影、替换元素（比如img）回流和重绘解析理解回流reflow：（也可以称之为重排）第一次确定节点的大小和位置，称之为布局（layout）。之后对节点的大小、位置修改重新计算称之为回流。什么情况下引起回流呢？比如DOM结构发生改变（添加新的节点或者移除节点）；比如改变了布局（修改了width、height、padding、font-size等值）比如窗口resize（修改了窗口的尺寸等）比如调用getComputedStyle方法获取尺寸、位置信息；理解重绘repaint：第一次渲染内容称之为绘制（paint）。之后重新渲染称之为重绘。什么情况下会引起重绘呢？比如修改背景色、文字颜色、边框颜色、样式等；回流一定会引起重绘，所以回流是一件很消耗性能的事情。所以在开发中要尽量避免发生回流：修改样式时尽量一次性修改 比如通过cssText修改，比如通过添加class修改尽量避免频繁的操作DOM我们可以在一个DocumentFragment或者父元素中将要操作的DOM操作完成，再一次性的操作；尽量避免通过getComputedStyle获取尺寸、位置等信息；对某些元素使用position的absolute或者fixed并不是不会引起回流，而是开销相对较小，不会对其他元素造成影响。合成和性能优化特殊解析–composite合成绘制的过程，可以将布局后的元素绘制到多个合成图层中。这是浏览器的一种优化手段；默认情况下，标准流中的内容都是被绘制在同一个图层（Layer）中的；而一些特殊的属性，会创建一个新的合成层（CompositingLayer），并且新的图层可以利用GPU来加速绘制；因为每个合成层都是单独渲染的；那么哪些属性可以形成新的合成层呢？常见的一些属性：3D transformsvideo、canvas、iframeopacity 动画转换时；position: fixedwill-change：一个实验性的属性，提前告诉浏览器元素可能发生哪些变化；animation 或transition 设置了opacity、transform；分层确实可以提高性能，但是它以内存管理为代价，因此不应作为web 性能优化策略的一部分过度使用。script元素和页面解析的关系我们现在已经知道了页面的渲染过程，但是JavaScript在哪里呢？事实上，浏览器在解析HTML的过程中，遇到了script元素是不能继续构建DOM树的；它会停止继续构建，首先下载JavaScript代码，并且执行JavaScript的脚本；只有等到JavaScript脚本执行结束后，才会继续解析HTML，构建DOM树；为什么要这样做呢？因为JavaScript的作用之一就是操作DOM，并且可以修改DOM；我们等到DOM树构建完成并且渲染再执行JavaScript，会造成严重的回流和重绘，影响页面的性能；会在遇到script元素时，优先下载和执行JavaScript代码，再继续构建DOM树；但是这个也往往会带来新的问题，特别是现代页面开发中：前的开发模式中（比如Vue、React），脚本往往比HTML页面更“重”，处理时间需要更长；会造成页面的解析阻塞，在脚本下载、执行完成之前，用户在界面上什么都看不到；为了解决这个问题，script元素给我们提供了两个属性（attribute）：defer和async。defer和async属性defer属性defer 属性告诉浏览器不要等待脚本下载，而继续解析HTML，构建DOM Tree。脚本会由浏览器来进行下载，但是不会阻塞DOM Tree的构建过程；如果脚本提前下载好了，它会等待DOM Tree构建完成，在DOMContentLoaded事件之前先执行defer中的代码；所以DOMContentLoaded总是会等待defer中的代码先执行完成 另外多个带defer的脚本是可以保持正确的顺序执行的。从某种角度来说，defer可以提高页面的性能，并且推荐放到head元素中；注意：defer仅适用于外部脚本，对于script默认内容会被忽略。async属性async 特性与 defer 有些类似，它也能够让脚本不阻塞页面。async是让一个脚本完全独立的：浏览器不会因 async 脚本而阻塞（与 defer 类似）；async脚本不能保证顺序，它是独立下载、独立运行，不会等待其他脚本；async不会能保证在DOMContentLoaded之前或者之后执行； defer通常用于需要在文档解析后操作DOM的JavaScript代码，并且对多个script文件有顺序要求的；async通常用于独立的脚本，对其他脚本，甚至DOM没有依赖的；感谢大家观看，我们下次见
这篇文章用来整理this指向相关面试题，我们请看内容面试题一var name = "window"; var person = { name: "person", sayName: function () { console.log(this.name); } }; function sayName() { var sss = person.sayName; sss(); // 绑定: 默认绑定, window -> window person.sayName(); // 绑定: 隐式绑定, person -> person (person.sayName)(); // 绑定: 隐式绑定, person -> person (b = person.sayName)(); // 术语: 间接函数引用, window -> window } sayName();上面代码执行之后依次打印window person person window解释一下，这里的第一个window是上面的person对象里面的sayName函数，那么我们这里调用的this为什么不是指向我们preson对象呢？原因是我们sayName方法使用环境，将sayName函数重新赋值给了一个变量sss,我们调用sss()执行sayName方法，当我们调用sss的时候我们当前的环境是在windows中，所以我们可以理解，假如我们直接调用person.sayName()才是打印person对象，也就是我们第二个打印出person的隐式绑定的方法。第二个person的由来：第二个person我们知道当小括号包裹一段内容这段内容可以看做一个整体，并且先运行，所以我们可以将第一个小括号里的内容先运行出来，没错他就是我们的sayName方法，众所周知，函数体在JavaScript中是堆内存，所以我们执行这里只是调用了这个内存地址，并不关person的事，然后我们将这个内存地址运行的时候就需要在后面加个新的小括号了，这里讲的就是函数的调用。最后一个window打印的时候，是将我们的person.sayName方法间接赋值给了一个全局变量b并且执行，假如在最后我们打印b变量会发现是ƒ () {console.log(this.name);}和我们打印sss的时候情况一模一样，所以就能体会到JavaScript实际使用中的玄妙了吧。面试题二var name = 'window' // {} -> 对象 // {} -> 代码块 var person1 = { name: 'person1', foo1: function () { console.log(this.name) }, foo2: () => console.log(this.name), foo3: function () { return function () { console.log(this.name) } }, foo4: function () { // console.log(this) // 第一个表达式this -> person1 // console.log(this) // 第二个表达式this -> person2 // console.log(this) // 第三个表达式this -> person1 return () => { console.log(this.name) } } } var person2 = { name: 'person2' } // 开始题目: person1.foo1(); // 隐式绑定: person1 person1.foo1.call(person2); // 显式绑定: person2 person1.foo2(); // 上层作用域: window person1.foo2.call(person2); // 上层作用域: window person1.foo3()(); // 默认绑定: window person1.foo3.call(person2)(); // 默认绑定: window person1.foo3().call(person2); // 显式绑定: person2 person1.foo4()(); // person1 person1.foo4.call(person2)(); // person2 person1.foo4().call(person2); // person1上面执行之后在控制台打印：person1 person2 window window window window person2 person1 person2 person1person1.foo1()这里打印person1,由于是由person1调用的foo1函数，是隐式调用的方式，this指向persion1对象。所以这里的this的name就是person1person1.foo1.call(person2)这里为什么会打印出person2呢，这是由于我们使用了call显式绑定，person2对象的name属性又是person2,我们又知道显式绑定的优先级高于隐式绑定，所以才打印我们的person1而不是person2person1.foo2()这里又为什么打印window呢？原因就是我们的箭头函数，箭头函数是没有作用域的，所以 foo2: () => console.log(this.name)中的this指向的就是window对象，那么有人又要有疑问了，箭头函数没有作用域的话他指向的name不应该是person1吗？不是的，在JavaScript中Object类型没有他的作用域的，我们的方法才具备块级作用域。所以就打印我们的window,这里的window是我们在顶级作用域定义的name，假如我们顶级没定义name的话，打印就的就是空。person1.foo2.call(person2)这里我们打印出来window,是为什么呢？由于我们看到使用了call显式调用数据，那不应该打印persion2吗？不是我们不能调用persion2,因为箭头函数没有作用域，这个特性要大于其他的特点。所以还是要乖乖打印顶级定义的name变量。person1.foo3()()这里打印我们window,原因是什么呢？person1.foo3函数调用返回了一个函数，注意这里返回的函数是一个函数地址，也就是把函数所对应的栈内存地址返回了，第二个括号调用我们的占内存地址，也就是我们的堆内存。由堆内存执行this。这里执行的this所对应的是我们的顶级作用域window，打印出的也就是变量name了person1.foo3.call(person2)()这里我们调用的不应该打印我们person2对应的值吗？不是的，原因是foo3调用返回是一个方法，不能用call即使使用了call所改变的也不是我们person1函数的name。所以还是打印的结果和person1.foo3()()一样都是window。person1.foo3().call(person2) 这里打印出来的就是我们的person2,为什么是这个结果呢，由于我们将person1.foo3调用了，并且使用call关键词显示绑定为person2对应的nameperson1.foo4()()这里输出我们的person1,因为返回的是一个箭头函数，又因为箭头函数是没有作用域的，所以这里的this就是指向我们的person1，打印我们的person1person1.foo4.call(person2)()这里person1.foo4使用了显示调用call为什么在这里可以使用person2的name，并且把他打印出来，person1.foo4返回的是箭头函数，也就是我们内存地址，在没有调用箭头函数之前将person2对象绑定到person1.foo4函数中。紧接着调用，就能打印出person2对应的值person1.foo4().call(person2)这里打印出person1,原因是箭头函数的问题，箭头函数没有作用域所以也就不能绑定this,显式绑定就失去了作用，依然打印person1不打印person2面试题三var name = 'window' /* 1.创建一个空的对象 2.将这个空的对象赋值给this 3.执行函数体中代码 4.将这个新的对象默认返回 */ function Person(name) { this.name = name this.foo1 = function () { console.log(this.name) }, this.foo2 = () => console.log(this.name), this.foo3 = function () { return function () { console.log(this.name) } }, this.foo4 = function () { return () => { console.log(this.name) } } } // person1/person都是对象(实例instance) var person1 = new Person('person1') var person2 = new Person('person2') // 面试题目: person1.foo1() // 隐式绑定: person1 person1.foo1.call(person2) // 显式绑定: person2 person1.foo2() // 上层作用域查找: person1 person1.foo2.call(person2) // 上层作用域查找: person1 person1.foo3()() // 默认绑定: window person1.foo3.call(person2)() // 默认绑定: window person1.foo3().call(person2) // 显式绑定: person2 person1.foo4()() // 上层作用域查找: person1(隐式绑定) person1.foo4.call(person2)() // 上层作用域查找: person2(显式绑定) person1.foo4().call(person2) // 上层作用域查找: person1(隐式绑定)上面代码执行后打印：person1 person2 person1 person1 window window person2 person1 person2 person1person1.foo1()这里打印我们的person1,为什么打印person1,由上面代码看出我们的person1是由一个方法调用出来的，这个类的参数是person1,Person类中的this就是指向我们的方法，我们person1调用foo(),这里foo1是有自己的作用域的，但是在自己作用域中没找到name,就到上级作用域找，找到person1person1.foo1.call(person2)打印person2是为什么呢？由于我们的foo1使用的是默认绑定打印this.name,call显式绑定person2,显式绑定优先级要高于隐式绑定，所以我们当然要调用person2的this.name打印person1.foo2()这里主要是由于我们的箭头函数没有作用域，直接去上级找this.name打印person1person1.foo2.call(person2)这里打印person1， person1.foo2是一个箭头函数，没有作用域，所以调用call也然并卵。还是打印person1中的nameperson1.foo3()()window为什么呢？person1.foo3返回的是一个方法，这个方法又在最顶层首先调用并且再次调用person1方法window为什么呢？person1.foo3返回的是一个方法，这个方法又在最顶层首先调用并且再次调用person1方法 person1.foo3.call(person2)()这里也是返回window,原因是person1.foo3并没调用函数就直接call绑定,返回的依然是window,依旧是默认值person1.foo3().call(person2)这里打印我们的person2，显式绑定和上面的操作不同这里调用foo3方法之后call，可以改变name的值，用了显示绑定。person1.foo4()()这里打印person1,返回的是箭头函数没有作用域，直接上层查找返回person1,属于隐式绑定person1.foo4.call(person2)()这里person1.foo4调用person2函数，虽然说person1.foo4返回的是一个箭头函数但是我们将person2绑定到了person1.foo4上，在调用之后this.name改变了,打印出我们的person2person1.foo4().call(person2)这里打印person1,方法person1.foo4先调用，因为是箭头函数所以call绑定就失效了。还是调用隐式绑定的person1面试题四var name = 'window' /* 1.创建一个空的对象 2.将这个空的对象赋值给this 3.执行函数体中代码 4.将这个新的对象默认返回 */ function Person(name) { this.name = name this.obj = { name: 'obj', foo1: function () { return function () { console.log(this.name) } }, foo2: function () { return () => { console.log(this.name) } } } } var person1 = new Person('person1') var person2 = new Person('person2') person1.obj.foo1()() // 默认绑定: window person1.obj.foo1.call(person2)() // 默认绑定: window person1.obj.foo1().call(person2) // 显式绑定: person2 person1.obj.foo2()() // 上层作用域查找: obj(隐式绑定) person1.obj.foo2.call(person2)() // 上层作用域查找: person2(显式绑定) person1.obj.foo2().call(person2) // 上层作用域查找: obj(隐式绑定)上述代码执行后将会打印出来：window window person2 obj person2 objperson1.obj.foo1()()这里有一对象person1调用obj对象，然而obj返回了一个函数，这个函数打印了this.name,调用obj对象又调用foo1,使用了默认绑定，返回的是顶层定义的name.person1.obj.foo1.call(person2)()这里打印的为什么和上面的一样呢？原因其实很简单我们在一个作用域调用另外一个作用域返回的方法，这里的作用域使用call显式绑定也就不起作用了，因为foo1不是用this.foo1 = 定义的。这里使用的默认绑定返回windowperson1.obj.foo1().call(person2)这里打印person2，为啥就起作用了，person1.obj.foo1是一个内存地址，内存地址调用之后，将返回的方法绑定到了person2上，这里用了显示绑定，返回person2person1.obj.foo2()()这里打印了obj,person1.obj.foo2返回了一个箭头函数，箭头函数没有作用域，直接在上层作用域查找，打印objperson1.obj.foo2.call(person2)()这里将person2绑定到person1.obj.foo2方法上，使用了显式绑定，打印person2person1.obj.foo2().call(person2)这里先调用person1.obj.foo2,然后再绑定person2这里绑定失败，所以打印obj使用了隐式绑定感谢大家观看，我们下次见
this指向的定义this是JavaScript中定义的关键字，作用就是谁调用指向谁，没用明确定义的对象的时候，就会指向默认对象，默认绑定全局对象。还是对他的定义有不是很理解的地方，就让我们仔细看这篇文章吧。this定义特点函数在调用时，JavaScript默认给this绑定一个值this的绑定和定义的位置没有关系this的绑定和调用方式以及调用的位置有关this是在运行时被绑定的从特点来看，this是调用函数才有，不调用就没有明确定义，在调用的时候调用方式和调用位置起到了重要作用。this的绑定规则既然是调用的时候才有，那么this绑定肯定有好多的绑定规则，并不是随便就能将this绑定到某个对象上。this绑定规则又分为：默认绑定隐式绑定显式绑定new绑定下面就具体了解这四种绑定方式：默认绑定默认绑定是什么呢？当独立函数的调用的时候，可以理解成函数没有绑定到某个对象上进行调用。这就是我们的默认调用我们看几个(常见的默认绑定)例子：// 定义函数 function foo(name) { console.log("foo函数:", this) } // 1.方式一: 直接调用 foo()上面这个我们就是window对象了，为什么呢，因为我们foo()这个函数是在window调用的。我们再看另外一个： // 定义函数 function foo(name) { console.log("foo函数:", this) } // 2.方式二: 通过对象调起 var obj = { name: "why" } obj.aaa = foo obj.aaa()在这里我们就是obj这个对象了，因为我们是在obj这个对象调用的。隐式绑定也就是它的调用位置中，是通过某个对象发起的函数调用。让我们看几个例子： function foo() { console.log("foo函数:", this) } var obj = { bar: foo } obj.bar()这里的this就是foo对象了显式绑定如果我们不希望在对象内部包含这个函数的引用，同时又希望在这个对象上进行强制调用的时候，我们就需要使用显式绑定了。常用的显式绑定有call和apply,还有不常用的显式绑定bindcall那么我们的call显式绑定如何使用呢，请看下面：// 显式绑定 var obj = { name: "why" } function foo() { console.log("foo函数:", this) } // 执行函数, 并且强制this就是obj对象 foo.call(obj) foo.call(123) foo.call("abc")这里我们如果传入参数123,或者abc这种参数的话，this会自动生成一个数组或者字符串类，并且打印出来类型。applyapply如何使用呢，其实apply函数和call函数使用方式差不多，单也有点区别。// call/apply function foo(name, age, height) { console.log("foo函数被调用:", this) console.log("打印参数:", name, age, height) } // ()调用 // foo("why", 18, 1.88) // apply // 第一个参数: 绑定this // 第二个参数: 传入额外的实参, 以数组的形式 // foo.apply("apply", ["kobe", 30, 1.98]) // call // 第一个参数: 绑定this // 参数列表: 后续的参数以多参数的形式传递, 会作为实参 foo.call("call", "james", 25, 2.05)由上面代码我们可以总结出call函数传参是foo.call("call", "james", 25, 2.05)这样传递，然而apply参数传递是foo.apply("apply", ["kobe", 30, 1.98])，传入的数组，这就是这两个的区别，相同点就是call和apply都是显式绑定。bind在 bind() 被调用时，这个新函数的 this 被指定为 bind() 的第一个参数，而其余参数将作为新函数的参数，供调用时使用。下面我们看一个示例：function foo(name, age, height, address) { console.log("foo:", this) console.log("参数:", name, age, height, address) } var obj = { name: "why" } // 需求: 调用foo时, 总是绑定到obj对象身上(不希望obj对象身上有函数) // 1.bind函数的基本使用 // var bar = foo.bind(obj) // bar() // this -> obj // 2.bind函数的其他参数(了解) var bar = foo.bind(obj, "kobe", 18, 1.88) bar("james")从上面我们可以看到，bind种是将一个函数绑定到了内存中，然后重新将这块内存赋值到一个新的变量，this指向直接指向了obj这个对象。new绑定JavaScript中的函数可以当做一个类的构造函数来使用，也就是使用new关键字。下面我们来看一个例子：/* 1.创建新的空对象 2.将this指向这个空对象 3.执行函数体中的代码 4.没有显示返回非空对象时, 默认返回这个对象 */ function foo() { this.name = "why" console.log("foo函数:", this) } new foo() 那么上面这个this是绑定在那里呢?是我们的foo函数，为什么呢？因为是我们new了一个foo()函数，发起者是new foo()这个动作，假如我们改成下面这种写法：/* 1.创建新的空对象 2.将this指向这个空对象 3.执行函数体中的代码 4.没有显式返回非空对象时, 默认返回这个对象 */ function foo() { this.name = "why" console.log("foo函数:", this) } foo()当我们没有new这个关键词的时候，当然就是指向我们的window对象了。大家明白了吧，那么我们这些个绑定方式优先级是什么样的呢？规则优先级this指向的优先级是：1.默认规则的优先级最低2.显式绑定优先级高于隐式绑定3.new绑定优先级高于隐式绑定4.new绑定优先级高于bind总结来说就是，new绑定>显式绑定>隐式绑定>默认绑定。如何来验证规则的优先级顺序呢？显式绑定高于隐式绑定function foo() { console.log("foo:", this) } // 1.显式绑定绑定的优先级高于隐式绑定 // 1.1.测试一:apply高于默认绑定 var obj = { foo: foo } obj.foo.apply("abc") obj.foo.call("abc")上面我们可以看出， this 将会是 "abc"，所以显式绑定大于隐式绑定。比如我们的apply/call/bind函数，这里我们注意bind函数，他将是返回一个新的函数。bind高于默认绑定function foo() { console.log("foo:", this) } // 1.2.测试二:bind高于默认绑定 var bar = foo.bind("aaa") var obj = { name: "why", baz: bar } obj.baz()从上面代码中我们可以看到，打印的foo是aaa,而不是window对象，所以bind是高于我们的默认绑定的。这里包括后面我们都将充分利用这种方式来验证this的绑定优先级。new绑定优先级高于隐式绑定// 2.new绑定优先级高于隐式绑定 var obj = { name: "why", foo: function() { console.log("foo:", this) console.log("foo:", this === obj) } } new obj.foo()这里打印出来的第一个this是我们的obj对象，接着判断当前环境是不是和obj完全相等，结果打印false,这里我们就可以断定new obj.foo()所执行的代码并不是隐式绑定所绑定的foo对像，而是重新生成了一个对象。所以，new绑定是高于隐式绑定的。new优先级高于bind我们先看下面这个例子：function foo() { console.log("foo:", this) } var bindFn = foo.bind("aaa") new bindFn()执行我们会发现打印出的内容是{},而不是aaa，说明我们执行new绑定是要高于bind绑定的，我们如果将new去掉,可以发现打印出来的是aaa,这可以说明bind绑定是大于默认绑定bind优先级高于apply/callfunction foo() { console.log("foo:", this) } var bindFn = foo.bind("aaa") bindFn.call("bbb")从上面代码打印来看，打印内容是aaa，所以我们可以得出bind绑定优先级是要大于call\apply的，所以我们的优先级更加详细的来讲应该是：new绑定>显式绑定(其中bind>aplay/call)>隐式绑定>默认绑定。这样我们的this绑定相关的就已经基本完善了,假如还是感觉有点迷惑，可以看JavaScript面试题这篇文章。感谢大家观看，我们下次见
重学Javascript基础（十三) 阶段性项目华为商城
01.商品列表-界面搭建 Document 首页 > 笔记本 分类： 笔记本 台式机 智慧屏 服务优惠： 仅看有货 分期免息 优惠商品 排序： 综合 最新 评论数 价格 HUAWEI MateBook E 12.6英寸 1 预订立省300元|12期免息 ¥6499 分期免息 赠送积分 3190人评论 96% 好评 HUAWEI MateBook E 12.6英寸 2 预订立省300元|12期免息 ¥6499 分期免息 赠送积分 3190人评论 96% 好评 HUAWEI MateBook E 12.6英寸 2 预订立省300元|12期免息 ¥6499 分期免息 赠送积分 3190人评论 96% 好评 HUAWEI MateBook E 12.6英寸 2 预订立省300元|12期免息 ¥6499 分期免息 赠送积分 3190人评论 96% 好评 02.商品列表-动态列表 Document 首页 > 笔记本 分类: 笔记本 台式机 智慧屏 服务优惠: 仅看有货 分期免息 优惠商品 排序: 综合 最新 评论数 价格 03.商品列表-动态列表(重构) Document 首页 > 笔记本 分类: 笔记本 台式机 智慧屏 服务优惠： 仅看有货 分期免息 优惠商品 排序： 综合 最新 评论数 价格 04.商品列表-服务优惠-点击 Document 首页 > 笔记本 分类： 笔记本 台式机 智慧屏 服务优惠： 仅看有货 分期免息 优惠商品 排序： 综合 最新 评论数 价格 05.商品列表-服务优惠-过滤 Document 首页 > 笔记本 分类： 笔记本 台式机 智慧屏 服务优惠： 仅看有货 分期免息 优惠商品 排序： 综合 最新 评论数 价格 06.轮播图-界面搭建 Document 上一个 下一个 07.轮播图-轮播控制 Document 上一个 下一个 08.轮播图-自动轮播 Document 上一个 下一个 09.轮播图-指示器展示 Document 上一个 下一个 10.轮播图-位移滚动 Document 上一个 下一个 11.轮播图-无限滚动 Document 上一个 下一个 12.商品列表-服务优惠-排序 Document 首页 > 笔记本 分类： 笔记本 台式机 智慧屏 服务优惠： 仅看有货 分期免息 优惠商品 排序： 综合 好评 评论数 价格 13.商品列表-服务优惠-排序(debug) Document 首页 > 笔记本 分类： 笔记本 台式机 智慧屏 服务优惠： 仅看有货 分期免息 优惠商品 排序： 综合 好评 评论数 价格 感谢大家观看，我们下次见
01.BOM-window对象的使用 Document 百度一下 关闭窗口 02.BOM-location对象的使用 Document 打开新的网页 替换新的网页 网页重新加载 03.BOM-history对象的使用 Document 修改history 返回上一级 04.BOM-navigator对象的使用 Document 05.BOM-screen对象的使用 Document 06.JSON的文件格式-简单值12307.JSON的文件格式-数组[ "123", 123 ]08.JSON的文件格式-对象{ "name":"why", "age":123, "friend":{ "name":"kobe", "age":null } }09.JSON-JSON的序列化 Document 10.JSON-stringify方法的补充 Document 11.JSON-parse方法的补充 Document 感谢大家观看，我们下次见