虽然早有准备,但还是被无情按在地上摩擦摩擦
作用域
作用域是指程序源代码中定义变量的区域。
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域
词法作用域(需要安静的理解下)
词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,不是在执行它们的作用域里运行,因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)。而JavaScript采用的就是词法作用域,也称为静态作用域。
词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。编译的词法分析阶段基本能够完全知道全部标识符在哪里以及如何声明的,从而能够预测在执行过程中如何对他们进行查找
因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。
而与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var name = 'Mr.Han'; function test(){ alert(name); var name = 'Mrs.Han'; alert(name); } test();
var value = 1; function foo() { console.log(value); }
function bar() { var value = 2; foo(); }
bar();
|
- 假设JavaScript采用静态作用域,让我们分析下执行过程:
执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。
假设JavaScript采用动态作用域,让我们分析下执行过程:
执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。
前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 1。
闭包特性
事件委托
利用js计算,最后一次性修改DOM,大大减少DOM交互,减少回流和重绘,提升网页性能,还能减少内存占用,使用事件委托,只要对元素的父级指定事件处理函数即可
事件委托原理
依赖事件冒泡原理实现的,事件冒泡指的是事件会从最深的节点处开始逐步往上传播事件,由于有这样的机制,我们可以只给最外面的元素添加事件,当内层的标签触发了某个事件,该事件会通过事件冒泡传播到最外层标签,从而触发真正的事件处理函数。这也就是事件委托的过程,子元素委托父级元素代为执行事件。
如何阻止冒泡 && 取消默认事件
w3c是event.stopPropagation,IE则是event.cancelBulle,事件的一个方法,作用是阻止目标元素的事件冒泡,但是不会阻止事件的默认行为,
W3c是event.preventDefault,IE则是event.rerturnValuee = false, 作用是取消一个目标元素的默认行为。只有元素本身有默认行为取消才有意义,什么元素具有默认行为呢?
比如 提交按钮
1 2
| event.stopPropagation() event.preventDefault
|
参考链接
实现new操作符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| export const newOperator = (ctor: Function, ...args: any[]) => { if (typeof ctor !== 'function') { throw('第一个参数必须为函数') } const newObj = Object.create(ctor.prototype); const returnObj = ctor.apply(newObj, args) return typeof returnObj === 'object' ? returnObj : newObj }
import { mockNewFn, newOperator } from '../utils/mockBind' const girl3 = newOperator(Girl, '测试下我的小女生3') console.log('gril3 —————— newOperator', girl3, girl3.greetName());
|
模拟实现apply/call
两者区别在于apply第二个参数传入的是数组,而call是一个一个参数传入的
模拟的时候注意:
- this 参数可以传 null,当为 null 的时候,视为指向 window
- 函数是可以有返回值的!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| Function.prototype.mockApply = function(context, args) { var context = context || window; var sym = Symbol(); context[sym] = this; let result = eval('context.sym(...args)') delete context[sym] return result }
const obj = { name: 'cpp' } function sayName(name) { alert(name) } Function.prototype.mockApply = mockApply sayName.mockApply(obj, ['菜鸟'])
Function.prototype.mockCall = function (context, ...args) { var context = context || window; context.fn = this; var result = eval('context.fn(...args)'); delete context.fn return result; }
import { mockApply, mockCall } from '../utils/mockBind'
(Function.prototype as any).mockApply = mockApply; (Function.prototype as any).mockCall = mockCall; const test1 = (sayName as any).mockApply(TestObj, ['chendaoeng mockApply', 30]) console.log('test1- mockApply', test1); const testMockCall = (sayName as any).mockCall(TestObj, 'chendaoeng mockCall', 30) console.log('testMockCall- mockCall', testMockCall);
|
实现bind函数
bind函数: bind()会创建一个方法,当这个新函数被调用的时候,bind()的第一个参数将作为它运行时的this,之后的序列参数会在传入的实参前传入作为它的参数。
需要理解下,bind函数有两种;
手写mockBind
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| export const mockBind = function (this: any, obj: any, ...firstArgs: any) { if (typeof this !== 'function') { throw new TypeError(`${this}.bind is not a function`) } const context = obj || window; const self = this; const bindFn = function(this: any, ...bindFnArg: any) { console.log('context传入的————', context); console.log('this————', this); console.log('self————', self); console.log('this instanceof self', this instanceof self); return self.apply(this instanceof self ? this : context, [...firstArgs, ...bindFnArg]) } bindFn.prototype = this.prototype return bindFn };
|
普通函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const TestObj = { name: 'cpp' } function sayName(name: string, age: number) { const str = `我的名字是${name},年龄是${age}` return { tip: str } } (Function.prototype as any).mockBind = mockBind const bindName = sayName.bind(TestObj, 'niaogege bind');
const testBind1 = bindName(29); console.log('第一种', testBind1); const bindName2 = (sayName as any).mockBind(TestObj, 'niaogege bind'); const testBind2 = bindName2(29); console.log('第一种', testBind2);
|
mockBind之后new操作符调用
1 2 3 4 5 6
| const newTestBind1 = new (bindName as any)(30); console.log(newTestBind1, 'newTestBind1', Object.prototype.toString.call(newTestBind1)) const mockBindName = (sayName as any).mockBind(TestObj, 'niaogege mockBind') const newTestBind2 = new mockBindName(30); console.log(newTestBind2, 'newTestBind2', Object.prototype.toString.call(newTestBind2));
|
vue渲染原理
vue数据双向绑定
参考链接