JS高级第三天 一、函数的定义和调用 1、函数的定义方式
函数的定义方法:
1. 函数声明
2. 函数表达式
3. 利用 Function 构造函数
1 2 3 4 5 6 7 8 9 10 11 12 function fn (a, b ) { return a + b; }; var fn1 = function ( ) { console .log('123' ); }; var fn2 = new Function ('a' , 'b' , 'console.log(a+b)' ); fn2(1 , 2 ); console .dir(fn2);
2、函数调用 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 function fn1 ( ) { console .log(123 ); } fn1(); var obj = { say: function ( ) { console .log('hello' ); } }; obj.say(); function Student (name, age ) { this .name = name; this .age = age; } var xm = new Student('小明' , 12 ); console .log(xm); var btn = document .querySelector('button' ); btn.addEventListener('click' , function ( ) { console .log('点击了' ); }); window .setTimeout(function ( ) { console .log('345' ); }, 1000 ); var num = 10 ; !(function ( ) { var num = 10 ; console .log('hi' ); })()
二、this指向 1、函数内部this指向
普通函数 指向window
对象中的函数调用 指向调用者
构造函数的调用 指向实例对象
绑定事件的处理函数 指向绑定者
定时器的处理函数 指向window
立即执行函数 指向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 39 40 41 42 43 <div>hello</div> <button>点击</button> <script> function fn1 ( ) { console .log(this ); } fn1(); var obj = { say: function ( ) { console .log('hello' ); } }; obj.say(); var foo = obj.say; foo(); var that; function Student (name, age ) { that = this ; this .name = name; this .age = age; } var xm = new Student('小明' , 12 ); console .log(xm); console .log(xm === that); var btn = document .querySelector('button' ); btn.addEventListener('click' , function ( ) { console .log('点击了' ); console .log(this ); }); window .setTimeout(function ( ) { console .log(this ); }, 1000 ); var num = 10 ; !(function ( ) { var num = 10 ; console .log(this ); })()
2、call改变this指向
fn.call([thisArg, arg1, arg2…])
作用:调用一个函数,并指定调用时 this 的值
参数: thisArg 函数中 this 的指定值; arg1, arg2…可选的参数列表
返回值:函数调用的结果
注意 :第一个参数 thisArg 如果不传或是 null、undefined,默认函数内 this 指向 window
3、apply 方法
fn.apply(thisArg,[ argsArray])
作用:调用一个函数,并指定调用时 this 的值
参数: thisArg 函数中 this 的指定值; argsArray 可选的参数数组(!!)
返回值:函数调用的结果
和call方法区别 :
call方法从第二个传递的是每个独立参数,而apply传递的是参数数组
在可以使用 call 的情况下, 都可以使用 apply 替代
1 2 3 4 5 6 7 8 9 10 11 function fn (a, b ) { console .log(this ); console .log(a, b); } fn.call({}, 2 , 3 ); fn.apply({}, [2 , 3 ]); var arr = [1 , 254 , 545 , 12 , 5 ]; var res = Math .max.apply(null , arr); console .log(res);
4、 bind方法
var newFn = fn.bind(thisArg,arg1, arg2, …)
作用:基于原函数创建一个新函数,这个新函数的 this 被指定为第一个参数, 其余参数作为实参传递给新函数
参数:thisArg 是新函数内 this 的预设值; arg1, arg2 是新函数预设传入的参数
返回值: 新函数(本身不会调用函数)
1 2 3 4 5 6 7 8 function fn (a, b ) { console .log(this ); console .log(a + b); } var newFn = fn.bind({}, 3 , 4 ); newFn(); fn(3 , 4 )
三、严格模式 1、定义 采用具有限制性JavaScript变体的一种方式,摆脱了以前的松散模式
1. 消除了 js 代码的不合理和不严谨地方,减少怪异行为
2. 消除了代码的不安全地方,保证代码安全运行
3. 提高编译器的效率,增加运行的速度
4.禁用 ECMAScript 未来版本中可能会定义的一些语法 class extends super 等
使用:在scrip代码块最前面添加’use strict’;
2、严格模式的变化
变量未声明不能直接赋值
变量必须先声明再使用
函数内的 this 默认指向 undefined
非函数内的 this 默认指向 undefined
构造函数和类只能加 new 进行使用
函数中的参数名不能重名
不允许在非函数的代码块内声明函数 (chrome 浏览器没有实现)
四、高阶函数 对其他函数进行操作的函数,主要有两种类型的高阶函数:
1. 把函数作为参数的函数
把函数作为返回值的函数
五、闭包 1、定义: 内部函数访问外部函数声明的变量,这种组合方式就是闭包
2、JS 中的垃圾回收机制(GC) 垃圾回收机制会定期(周期性)找出那些不再引用到的内存(变量),然后释放其内存
3、闭包产生的原因 (1) 当一个函数内的声明的变量没有被其他函数引用时,那么调用完这个函数后,所有的局部变量就会被垃圾回收机制清除;
(2) 一旦这个变量被另外一个函数所引用,这个变量的值会始终保存在内存中,不会被垃圾回收机制回收,从而形成了闭包
4、案例:
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 <button>按钮</button> <button>按钮</button> <button>按钮</button> <button>按钮</button> <button>按钮</button> <script> var btns = document .querySelectorAll('button' ); for (var i = 0 ; i < btns.length; i++) { btns[i].index = i; btns[i].onclick = function ( ) { console .log(this .index); } } for (var i = 0 ; i < btns.length; i++) { (function (i ) { btns[i].onclick = function ( ) { console .log(i); } })(i) }
打车起步价 8(3公里内),之后每多一公里增加 5 块钱,用户输入公里数就可以得出打车价格,如果有拥堵的情况,在之前价格的基础上再多收取 10 块钱拥堵费
封装一个求正常时打车价格 和 拥挤时打车价格的对象, 并全局中不能访问到起步价和总价
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var obj = (function ( ) { var total = 0 ; var start = 8 ; return { price: function (km ) { total = km <= 3 ? start : (km - 3 ) * 5 + start; return total; }, busyPrice: function (isBusy ) { total = isBusy ? total + 10 : total; return total; } } })() console .log(obj.price(20 )); console .log(obj.busyPrice(true ));
六、递归函数
函数内部自己调用自己,作用和循环效果类似
递归很容易发生 “栈溢出” 错误,所以和 while 循环类似必须要添加中断条件
1 2 3 4 5 6 7 8 9 function fn2 (n, m ) { if (n == m) { return m; } return n * fn2(n + 1 , m); } var res2 = fn2(1 , 5 ); console .log(res2);
1 2 3 4 5 6 7 8 9 10 11 function fn (n ) { if (n == 0 ) return 0 ; if (n == 1 ) return 1 ; return fn(n - 1 ) + fn(n - 2 ); } console .log(fn(4 ));
七、拷贝 简单数据类型都是直接拷贝,不区分深浅拷贝
1、浅拷贝 只拷贝对象一层的数据,复杂数据类型只拷贝内存地址值(引用同一个对象)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var obj = { id: 1 , name: '小明' , data: { id: 2 , age: 19 } } var obj2 = {}; for (var key in obj) { obj2[key] = obj[key] } obj.data.id = 10 ; console .log(obj2.data.id);
2、深拷贝
拷贝对象多层的数据,遇到复杂数据类型会继续新建一个空间,拷贝里面每一层的属性和值
和浅拷贝的区别
深拷贝后的对象和原对象是完全隔离的,各自互不影响, 而浅拷贝中所有复杂数据类型的值,都是共同引用的
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 39 40 41 var obj1 = { id: 1 , name: '小明' , data: { id: 2 , age: 19 , goods: { id: 10 } }, arr: [1 , 2 , 3 ] } var obj2 = {} function deepClone (obj1, obj2 ) { for (var key in obj1) { var temp = obj1[key]; if (Array .isArray(temp)) { obj2[key] = []; deepClone(temp, obj2[key]); } else if (temp instanceof Object ) { obj2 = {}; deepClone(temp, obj2[key]); } else { obj2[key] = temp; } } }; deepClone(obj1, obj2); obj1.data.id = 10 ; obj1.arr[0 ] = 100 ; console .log(obj2.data.id); console .log(obj2.arr[0 ]);
3、补充
1 2 3 4 5 6 7 8 9 10 11 12 var obj = { a: 1 , b: 2 , c: { d: 4 } } obj.c.d = 10 ; var newObj = {...obj }; console .log(newObj);
深拷贝快速实现
JSON.parse(JSON.stringify(obj))
JSON.stringify()可以把复杂数据类型转成字符串
JSON.parse()可以把字符串再转成对象
1 2 3 var obj2 = JSON .parse(JSON .stringify(obj)) obj.c.d = 20 ; console .log(obj2);