1.javascript基础
1.1执行上下文/作用域/闭包/变量提升
作用域:是一套规则,这套规则用来管理引擎如何在当前作用域以内以及嵌套的子作用域中根据标识符名称进行变量查找。
作用域共有两种主要的工作模型:
1.词法作用域:作用域在编写代码的时候确定的
2.动态作用域:作用域在代码执行的时候确定的
js是词法作用域
。。。
变量提升:函数执行前会有两个阶段的任务
第一个阶段是编译阶段任务,第二个阶段是执行阶段的任务
这意味着无论作用域中的声明出现在什么地方,都将在代码本身执行前首先进行处理。可以将这个过程形象的想像成所有的声明(变量和函数)都会被“移动”到各自作用域的顶端,这个过程被成为提升。(声明本身会被提升,而包含函数表达式的赋值在内的赋值操作并不会提升。避免重复声明,特别是普通声明和函数生命混在一起。)
闭包:闭包是作用域的一部分。
1.函数声明的时候,会生成独立的作用域。
2.同一作用域的对象可以相互访问。
3.作用域呈层级包含状态,形成作用域链,子作用域的对象可以访问父作用域的对象,反之不能;另外作用域会使用最近的父作用域的对象。
闭包的形成是:持有对作用域以外的地方的执行。
将内部函数,传递到所在此法作用域以外,持有对原定义作用域的引用。无论在何处执行这个函数都会形成闭包。
闭包的运用:jQuery框架(无论何时何地,将(访问它们各自此法作用域的)函数当作第一级的值类型并到处传递)、定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或者其他的异步(或者同步)任务中,只要调用了回调函数,实际上就是在使用闭包。
循环和闭包:延迟函数的回调会在循环结束时才执行。事实上,当定时器运行时即使每个迭代中执行的是setTimeout(...,0),所有的回调函数依然是循环结束后才执行,访问的是共享的全局作用域。根据作用域的工作原理,使用闭包作用域,在循环的过程中为每个迭代创建一个闭包作用域,eg可以声明一个立即执行函数,将i传递进去。
模块:实现模块的方法,模块暴漏
1.返回对象字面量,返回的对象包含内部函数,而不是对内部数据的的引用。保持内部数据变量是隐藏切私有的状态,可以将这个对象类型的返回值看作本质上是模块公共的API。(返回一个含有属性引用的对象的方式来将函数传递到此法作用域外部。)
2.也可以直接返回内部函数,(函数本身也是对象,本身也可以拥有属性)
模块模式需要两个必要条件:1.必须有外部的封闭函数,该函数必须至少调用一次(每次调用都会创建一个新的模块实例)2.封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。(一个具有函数属性的对象本身并不是真正的模块。从方便观察的角度看,一个从函数调用所返回的,只有数据属性没有闭包函数的对象并不是真正的模块)
3.模块的另一个简单但强大的用法是命名将要作为公共API的对象。通过在模块实例的内部保留对内部公共API对象的内部引用,可以从对象模块实例进行修改,包括添加、删除方法和属性、以及修改他们的值。
4.现代的模块机制(函数闭包模块):核心modules[name]=impl.apply(impl,deps)。为了模块的定义引入了包装函数(可以传入任何依赖),并且将返回值,也就是模块的API,储存在一个根据名字管理的模块对象中。模块有两个特征:1.为创建内部作用域而调用了一个包装函数。2.包装函数的返回值必须至少包含一个对内部函数的引用,这样就会创建涵盖整个包装函数内部作用域的闭包。
高阶函数:函数作为参数传递,函数作为返回值传递。
AOP:把一个函数动态织入到另一个函数中,可以用原型实现
柯里化:计算函数,接受部分函授,延迟执行,最后返回接收剩余参数的函数,一次性执行。原理是接收部分参数在函数形成闭包,保存起来,最后一次性执行。
反柯里化:范化的过程,被柯里化的函数,接受多个参数,目的是创建更普适性的函数,可以被不同的对象使用。call、apply
防抖:多次执行,变为最后一次执行
截流:多次执行,变成每隔一段时间执行
www.cnblogs.com/zhihaospace…
1.2this/call/apply/bind
隐式“传递问题”
运用词法作用域也可以解决隐式“传递问题”,返回舒适区。
this的解决:this提供了一种更优雅的方式来隐式“传递”,一个对象引用。因此可以将API设计得更加简洁并且易于复用。当函数调用时,会创建活动记录(执行上下文),这个函数包含函数在哪里调用(调用栈)、函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中调用。
**this:**是在调用时被绑定的,完全取决于函数调用位置(也就是函数的调用方法)
**默认绑定:**独立调用函数。
**隐式绑定:**调用位置是否有上下文对象。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。(this间接绑定到这个对象上)
*对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
隐式丢失:一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局或者underfined上,取决于是否严格模式。
*隐式绑定赋值,有两种情况:1.隐式绑定赋值给一个变量,不带任何修饰符的函数调用,调用变量相当于默认绑定。2.参数传递也是一种隐式赋值。回调函数丢失this绑定是非常常见的。(无法控制回调函数的执行方式,因此就没有办法控制调用位置得以期望的绑定)。隐式绑定赋值时,记得绑定this。
**显示绑定:**在某个对象上强制调用函数。call(),apply()。解决在函数内部包含函数引用。
call()、apply()使用方法:第一个参数是一个对象,this的绑定对象。call()后面的参数是若干指定的参数值。apply()是包含多个参数的数组。
bind硬绑定:是显示绑定的强制绑定。在某个对象上强制绑定函数
方法:1.创建包裹函数,负责接收参数并返回值。
function foo {return foo.apply(ob,arguments)} 2.创建一个可以重复使用的辅助函数。返回函数的返回硬绑定 function bind(fn,obj){ return function(){ return fn.apply(obj,arguments) }}
为什么需要bind(this)
作用域的问题,foo() {} 与 const foo = () => {}里面的this作用域不一样,foo() {}里面使用外部成员,需要bind(this),直接使用的this作用域仅在该方法内部
确保回调函数使用指定的this,bind、内置函数的可选参数(通常称为“上下文”)。原理都是通过call()、apply()实现了显示绑定。
bind(...)的功能之一就是可以把除了第一个参数(第一个参数用于绑定this),之外的其他参数都穿给下层的函数(这种技术成为“部分应用”,是“柯里化”的一种)。
this忽略的情况
apply()数组展开成参数 foo.apply(null,[2,3]) es6可用...代替
使用bind(...)进行柯里化 var bar = foo.bind(null,2) bar(3) function foo(a,b){...}
更安全的this,创建一个空的非委托的对象。 var ø = Object.create(null) foo.apply(ø,[2,3])
**new绑定:**使用new函数调用函数,执行的操作
1.创建(或者说构造)一个新的全新的对象。
2.这个新对象会执行[[prototype]]连接。
3.这个新对象绑定到函数调用的this。
4.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
ES5中内置的Function.protorype.bind(...)
软绑定:
**优先级:**判断一个函数的this绑定,需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面的四条规则来判断this的绑定对象。
1.有new调用?绑定到新创建的对象。
2.由call或者apply(或者bind)调用?绑定到指定的对象。
3.由上下文对象调用?绑定到那个上下文对象。
4.默认:严格模式下绑定到underfined,否则绑定到全局对象。
*ES6中箭头函数并不会使用四则标准的绑定规则,而是根据当前此法作用来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定的是什么)。这其实和ES6之前代码中的self=this机制一样。
2.原型链中的 this
2.1prototype 被定义为:给其它对象提供共享属性的对象。
因此,prototype 描述的是两个对象之间的某种关系(其中一个,为另一个提供属性访问权限)。它是类似 father 父亲一样的称谓,而不是具有超能力的异常对象。
所有对象,都可以作为另一个对象的 prototype 来用。
规范中明确描述了所有对象,都有一个隐式引用,它被称之为这个对象的 prototype 原型。
也就是说,所谓的隐式,是指不是由开发者(你和我)亲自创建/操作。
2.2所有 object 对象都有一个隐式引用
在控制台却可以发现它有 __proto__ 属性,这意味着 obj 被隐式地挂载了另一个对象的引用,置于 __proto__ 属性中。
2.3、prototype chain 原型链
prototype 对象也有自己的隐式引用,有自己的 prototype 对象。,构成了对象的原型的原型的原型的链条,直到某个对象的隐式引用为 null,整个链条终止。
JavaScript 中的 Object 和 Function 就是典型的函数对象。
Object、Function和其它对象的关系可以归纳为下面四点:
-
一切对象都最终继承自Object对象,Object对象直接继承自根源对象null
-
一切函数对象(包括Object对象)都直接继承自Function对象
-
Object对象直接继承自Function对象
-
Function对象直接继承自己,最终继承自Object对象
-
一切对象都继承自Object对象是因为一切对象的原型链最终都是
.... → Object.prototype → null,包括Function对象,只是Function的原型链稍微绕了一点,Function的原型链为Function → Function.prototype → Object.prototype → null,它与其它对象的特别之处就在于它的构造器为自己,即直接继承了自己,最终继承于Object,上面的原型链可以在浏览器验证:
2. Object继承自Function,Object的原型链为 Object → Function.prototype → Object.prototype → null,原型链又绕回来了,并且跟第一点没有冲突。可以说Object和Function是互相继承的关系。
首先 Object和 Function都是构造函数,而所有的构造函数的都是 Function的实例对象. 因此 Object是 Function的实例对象 Function.prototype是 Object的实例对象
每一个函数都有一个prototype属性,该属性的内部有一个指针指向一个对象,所指向的对象称为原型对象。
这个对象包含了可以由该构造函数的所有实例共享的属性和方法
现在浏览器中都实现了 __proto__ 属性来让我们访问这个属性,但是我们最好不要使用这 个属性,因为它不是规范中规定的。ES5 中新增了一个 Object.getPrototypeOf() 方法,我们可以通过这个方法来获取对 象的原型。
每一个对象都有一个constructor属性,函数也是对象,该属性指向的是用于创建该对象的构造函数,所引用的值是创建该对象的那个构造函数的原型对象中的constructor的值
要想全部理解 instanceof 的原理,除了我们刚刚提到的实现原理,我们还需要知道 JavaScript 的原型继承原理。
我们知道每个 JavaScript 对象均有一个隐式的 __proto__ 原型属性,而显式的原型属性是 prototype,只有 Object.prototype.__proto__ 属性在未修改的情况下为 null 值。根据图上的原理,我们来梳理上面提到的几个有趣的 instanceof 使用的例子。
-
Object instanceof Object由图可知,Object 的
prototype属性是Object.prototype, 而由于 Object 本身是一个函数,由 Function 所创建,所以Object.__proto__的值是Function.prototype,而Function.prototype的__proto__属性是Object.prototype,所以我们可以判断出,Object instanceof Object的结果是 true 。用代码简单的表示一下