-
Notifications
You must be signed in to change notification settings - Fork 0
/
函数的扩展.html
443 lines (385 loc) · 12.1 KB
/
函数的扩展.html
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
p{ white-space: pre-line;}
</style>
</head>
<body>
<p>
一 函数参数的默认值
1.基本用法
2.参数默认值是惰性求值得 每次调用函数都会重新计算x+1
3.与解构赋值的默认值结合使用
4.参数的默认位置 通常定义默认值得参数 放在函数的尾参数
5.函数的length属性 将返回没有指定默认值得参数个数
6.作用域
7. 应用
</p>
<script>
//默认值的写的方式 基本用法
{
function log(x,y='word') {
console.log(x,y)
}
log('hello')//hello word
}
//惰性求值
{
let x=0
function test(p=x+1) {
console.log(p)
}
test()//1
x=1
test()//2
}
//与结构结合
{
console.log("=========与结构结合")
function test(x,y=5) {
console.log(x,y)
}
test({})
test({x:20,y:2})
//参数的默认值是空的对象
function m1({x = 1, y = 1} = {}) {
return [x, y];
}
//参数的默认值是一个有具体属性的对象,但是设置对象结构赋值的默认值 没有对x y赋值
function m2({x, y} = { x: 1, y: 1 }) {
return [x, y];
}
console.log("111111111")
//函数没有参数的情况
console.log(m1())//[1,1]
console.log(m2())//[1,1]
console.log("222222222")
//x y都有值的情况
console.log(m1({x: 3, y: 8})) // [3, 8]
console.log(m2({x: 3, y: 8})) // [3, 8]
console.log("333333333")
// x有值 y没值
console.log(m1({x: 3})) // [3, 1]
console.log(m2({x: 3})) // [3, undefined]
console.log("4444")
//x,y 都没有值
console.log(m1({})) // [1, 1]
console.log(m2({})) // [undefined, undefined]
}
//函数的length
{
// console.log((function (a) {}).length) ; // 1
// console.log((function (a = 5) {})).length ;// 0
// (function (a, b, c = 5) {}).length; // 2
}
//作用域
{
console.log("==========作用域=============")
let foo='outer';
// 匿名函数里面的foo指向函数外层 j箭头函数
//函数参数形成的单独作用域里面,并没有定义变量foo,所以foo指向外层的全局变量foo,因此输出outer。
function bar(func=()=>foo+'test') {
let foo='inner';
console.log(func())
}
console.log(foo)
bar()//outer
function bar2(func=(foo)=>foo) {
let foo='inner';
console.log(func(foo))
}
bar2()//inner
}
{
console.log("==========作用域222=============")
let x = '1';
function foo(x, y = function() { x = '2'; }) {
x = '3';
y();
console.log(x);
}
foo() // 2
console.log(x) // 1
}
//应用
{
console.log("==========应用=============")
function throwError() {
throw new Error("missParammter")
}
function test(param=throwError()) {
return param
}
try{
test()//Error: missParammter
}catch (e){
console.log(e)
}
}
</script>
二. rest函数
1. ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。
rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
2.rest 参数之后不能再有其他参数(即只能是最后一个参数) 否则会报错
<script>
//获取多余参数
{
function add(...value) {
let sum=0
for (let a of value){
sum+=a
}
return sum
}
console.log(add(1,2,3))
}
//获取替换arguments变量
{
function sortNumbers1() {
console.log(Array.prototype.slice.call(arguments).sort())
}
function sortNumbers2(...numbers) {
console.log(numbers.sort())
}
// rest参数的写法
sortNumbers1(3,1,2)
sortNumbers2(4,6,5)
}
</script>
<p>
三.严格模式
es6 只要函数参数使用默认,解构赋值,或者扩展运算符那么函数内部
就不能显式设定为严格模式,否则会报错
原因:参数使用默认时 函数执行的时候,先执行函数参数,然后再执行函数体。
但是只有从函数体中,才能知道参数是否应该以严格模式执行,但是参数却应该先于函数体执行
解决方式
第一种是设定全局性的严格模式,这是合法的。
第二种是把函数包在一个无参数的立即执行函数里面
</p>
<script>
//严格模式test
function doSome(a,b=a) {
// 'use strict'//使用便会报错
}
//解决方式一
'use strict';
function doSomething(a, b = a) {
// code
}
//解决方式二
const doSomething2 = (function () {
'use strict';
return function(value = 42) {
return value;
};
}());
</script>
<p>
四.name 属性
1.函数的name属性,返回该函数的函数名
2.如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。
3.Function构造函数返回的函数实例,name属性的值为anonymous。
4.bind返回的函数,name属性值会加上bound前缀。
</p>
<script>
//name 属性
{
console.log("===========name 属性")
//正常返回
function foo() {}
console.log(foo.name) // "foo"
//匿名函数赋值给一个变量
var f = function () {};
console.log(f.name)//f
//name属性的值为anonymous
console.log((new Function).name)//anonymous
//bind返回的函数
function foo2() {};
console.log(foo2.bind({}).name) // "bound foo2"
console.log((function(){}).bind({}).name) // "bound "
}
</script>
<p>
五.箭头函数
基本语法
1.一个圆括号代表参数部分 箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
2.特殊情况
1.由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
2. 可能会运行但是 会得到错误的结果
3注意点
3.1. 函数体的this队形,就是定义时所在的对象,而不是使用时所在的对象
(this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this)
3.2. 不可以当构造函数,也就是说,不可以使用new命令
3.3. 不可以使用argument对象,该对象再函数体内不存在,如果要用,可以用rest参数代替
3.4. 不可以使用yueld函数,因此箭头函数不能作Generator函数
3.5. 不能用call() apply() bind()这个方法改变this的指向
4.不适用场合
4.1 定义函数的方法,且该方法内部包括this
4.2 需要动态this的时候,也不应使用箭头函数
5 嵌套的箭头函数
1.多重嵌套的函数
2.管道机制需要后期了解//
</p>
<script>
//5.1
{
console.log("===========箭头函数 基本用法")
//报错的情况
// let getTempItem = id => { id: id, name: "Temp" };
let s2=10
function Timer() {
this.s1=0
this.s2=0
setInterval(()=>this.s1++,1000)
//此时取的是Timer内的this
setInterval(function () {
this.s2++//this指向运行时所在的作用域(即全局对象)
},1000)
}
let timer=new Timer()
setTimeout(() => console.log('s1: ', timer.s1), 3100);//s1: 3
setTimeout(() => console.log('s2: ', timer.s2), 3100);//s2: 0
}
// {
// function foo() {
// return () => {
// return () => {
// return () => {
// console.log('id:', this.id);
// };
// };
// };
// }
// let f = foo.call({id: 1});
// debugger
// }
//5.3.5
{
// console.log("=========== 3.5")
// (function() {
// return [
// (() => console.log(this.x)).bind({ x: 'inner' })()
// ];
// }).call({ x: 'outer' });
}
//5.4.1 5.4.2
{
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
// var button = document.getElementById('press');
// button.addEventListener('click', () => {
// this.classList.toggle('on');
// });
//点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。
// 如果改成普通函数,this就会动态指向被点击的按钮对象。
}
//嵌套的箭头函数
{
//多重嵌套函数
let insert=(value)=>({into:(array)=>{
console.log("test箭头函数"+value+array)
}}
)
insert(2).into(3)
//管道机制
}
</script>
<p>
6.双冒泡运算符
1.定义
函数绑定运算符是并排的两个冒号(::)
(1)双冒号左边是一个对象,右边是一个函数。
该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面
用来取代call apply, bind调用
(2)双冒号左边为空,右边是一个对象的方法,则等于将方法绑定在该对象上面
(3)如果双冒号运算符的运算结果,还是一个对象,就可以采用链式写法//暂时没深入
</p>
<script>
//基础语法(1)
{
// foo::bar;// 等同于
// bar.bind(foo);
// foo::bar(...arguments);// 等同于
// bar.apply(foo, arguments)
// const hasOwnProperty = Object.prototype.hasOwnProperty;
// function hasOwn(obj, key) {
// return obj::hasOwnProperty(key);
// }
}
//(2)
{
// let method = obj::obj.foo;// 等同于
// var method = ::obj.foo;
// let log=::obj.foo//等同于
// let log=console.log.bind(console)
}
</script>
<p>
7.尾调用优化
1.概念:某个函数的最后一步是调用另一个函数。(不一定出现在函数尾部)
2.尾调用优化
</p>
<script>
{
console.log("===========尾调用优化")
// 情况二
function g(x){
console.log("======"+x);
}
// 情况三
function f(x){
g(x);
}
f(2)
}
</script>
=======
<p>
尾递归优化的实现
尾递归优化只在严格模式下生效,在不支持的环境中,通过将递归改成循环的方式
</p>
<script>
console.log("尾递归优化的实现")
//递归调用
// function sum(x, y) {
// if (y > 0) {
// return sum(x + 1, y - 1);
// } else {
// return x;
// }
// }
//
// sum(1, 100000)//Uncaught RangeError: Maximum call stack size exceeded
</script>
<script>
//递归更改为蹦床函数执行 避免出现堆栈溢出
{
//蹦床函数将递归执行转为循环执行
function trampoline(f) {
while (f && f instanceof Function) {
f = f();
}
return f;
}
function sum(x, y) {
if (y > 0) {
return sum.bind(null, x + 1, y - 1);
} else {
return x;
}
}
}
console.log(trampoline(sum(1, 100000)))
</script>
<script>
</script>
>>>>>>> 3dd75d9013819f67126f24a00e6bba204ba870f6
</body>
</html>