constminIndex = (numbers) => { var index = 0 for (let i = 1; i < numbers.length; i++) { if (numbers[i] < numbers[index]) { index = i } } return index }
constswap = (array, i, j) => { let temp = array[i] array[i] = array[j] array[j] = temp }
constsort = (numbers) => { for (let i = 0; i < numbers.length - 1; i++) { console.log(`----`) console.log(`i: ${i}`) let index = minIndex(numbers.slice(i)) + i console.log(`index: ${index}`) console.log(`min: ${numbers[index]}`) if (index !== i) { swap(numbers, index, i) console.log(`swap ${index}: ${i}`) console.log(numbers) } } return numbers }
constcountingSort = arr => { let hashTable = {}, max = 0, result = [] for (let i = 0; i < arr.length; i++) { if (!(arr[i] in hashTable)) { hashTable[arr[i]] = 1 } else { hashTable[arr[i]] += 1 } if (arr[i] > max) { max = arr[i] } } for (let j = 0; j <= max; j++) { if (j in hashTable) { for (let i = 0; i < hashTable[j]; i++) { result.push(j)
// 例1 functionfn(){ let a = 1; } console.log(a) // a 不存在 // 问: 是不是因为 fn 没执行导致 // 答: 就算 fn 执行了,也访问不到作用域里面的 a
1
// 例2function f1(){ let a = 1; function f2(){ let a = 2; console.log(a); } console.log(a); a = 3; f2(); console.log(a)}f1()// 打印结果为 /* 1 2 3*/
1
// 例3function f1(){ let a = 1; function f2(){ let a = 2; function f3(){ console.log(a) } a = 22; f3(); } console.log(a); a = 100; f2();}f1();/* 打印机结果为122*/
functionf1(){ let a = 1; functionf2(){ // let a = 2; function f3(){ console.log(a) }// /* 如果一个函数用到外部的变量 那么这个函数加这个变量 就叫做 闭包 左边的 a 和 f3 组成了 闭包 */ a = 22; f3(); } console.log(a); a = 100; f2();}f1();
形式参数
形式参数意思式非实际参数
1
functionadd (x, y){ return x + y;}// 其中 x 和 y 就是形参,因为并不是时机的参数add(1, 2);// 调用 add 时,1 和 2 是实际参数,会被复制给 x y// 上面代码近似等于下面代码function add(){ var x = arguments[0] var y = arguments[1] return x + y}
let person = { name: 'frank', sayHi(){ console.log('hello, i am ' + person.name); }}person.sayHi()/* 分析可以用直接保存了对象地址的 变量 获取 'name'这种办法简称为 引用*/
问题一
1
let sayHi = function(){ console.log('hello, i am ' + /* person*/.name)}let person = { name: 'frank', 'sayHi': sayHi.}/*分析person 如果改名,sayHi 函数就挂了sayHi 函数甚至有可能在另一个文件里所以我们不希望 sayHi 函数里出现 person 引用*/
问题二
1
classPerosn { constructot(name){ this.name = name // 这里的 this 是 new 强制指定的 } sayHi(){ console.log(/*????*/) }}/*分析这里只有类,还没创建对象,故不可能获取对象的引用那么如何拿到对象的 name ?*//*需要一种办法拿到对象这样才能获取对象的 name 属性*/
一种土方法,用参数
1
// 对象let person = { name: 'frank', sayHi(p) { console.log('hello, i am ' + p.name) }}person.sayHi(person)// 类class Person { constructor(name){ this.name = name } sayHi(p) { console.log('hello, i am ' + p.name) }}let person = new Person('frank')person.sayHi(person)
JS 在每个函数里加了 this
1
// 用 this 获取那个对象let person = { name: 'frank', sayHi(/*this*/){ console.log('hello, i am ' + this.name) }}person.sayHi()/*person.sahHi()相当于 person.sayHi(person)然后 person 被传给 this 了 (person 是个地址)这样每个函数都能用 this 获取一个未知对象的引用了*/// person.sayHi() 会隐式地把 person 作为 this 传给 sayHi// 方便 sayHi 获取 person 对应的对象
总结
我们想让函数获取对象的引用
但是并不想通过变量名做到
Python 通过额外的 self 参数做到
JS 通过额外的 this 做到:
person.sayHi() 会把person 自动传给 sayHi, sayHi 可以通过 this 引用 person
其他
注意 person.sayHi 和 person.sayHi() 的区别
注意 person.sayHi() 的短句 (person.sayHi)()
call 指定 this
1
// 哪个对let person = { name: 'frank', sayHi(/*this*/){ console.log('hello, i am ' + this.name) }}person.sayHi()Person.sayHi(person)// 省略形式的反而是对的// 两种调用方式person.sayHi()// 会自动把 person 传到函数里, 作为 thisperson.sayHi.call(person)person.sayHi.call({name: 'evan'})// 需要手 动把 person 传到函数里,作为 this// 推荐使用
在每次 for 循环中,都将异步代码(setTimeout)放入任务队列中,所以任务队列中有 6 个 setTimeout 即有 6 个 console.log(i)。
在每次 for 循环中将 setTimeout 里的代码 console.log(i) 放入任务队列时,i 的值是不一样的,当 JS 引擎开始执行任务队列中代码时,会在当前作用域中找变量 i ,但当前 for 循环的作用域中没有对变量 i 的进行定义,这个时候会在创造该函数的作用域中寻找 i,找到的是 let i,这时的 i 时全局变量,并且值已经确定为 6。所以打印出 6 个 6。
for( let i = 0; i < 6; i++ ){ setTimeout( ()=> { console.log(i); }, 0); }
let 的作用域是块作用域,能作用到 for 循环的子块中。
let 的作用于是块作用域,所以 setTimeout 被放到 任务队列的同时,let 定义的 i 值 也会跟随 setTimeout 进入队列。所以每次循环后队列里的 setTimeout 里的 i 值是不一样的。而 var 定义的 i 是无法进入的。(浅显易懂)
for 循环头部的 let 不仅将 i 绑定到 for 循环中,事实上它将其重新绑定到循环体的每一次迭代中,确保上一次迭代结束的值被重新赋值。setTimeout 里面的函数属于一个新的域,通过 var 定义的变量或全局变量是无法传入到这个函数执行,通过使用 let 来声明块变量能作用于这个块,所以箭头函数就能使用 i 这个变量,所以每次的 i 值不一样。
解决方法二 使用立即执行函数,即闭包
1 2 3 4 5 6 7 8
let i = 0; for( i = 0; i < 6; i++){ (function (j){ setTimeout( () => { console.log(j); }, 0); })(i); }
因为 setTimeout 是异步执行,所以让它立即执行就可以了。
通过闭包,将 i 的变量驻留在内存中,当输出 j 时,引用的是外部函数的变量值 i,i 的值是根据循环来的,执行 setTimeout 时已经确定了里面的的输出了。
解决方法三 setTimeout 第三个参数
1 2 3 4 5 6 7
let i = 0; for (i = 0; i < 6; i++){ setTimeout( (i) => { console.log(i); }, 0, i); } // 将每次的 i 值传入作用域。
解决方法四 try catch
1 2 3 4 5 6 7 8 9 10 11
let i = 0; for(i = 0; i < 6; i++){ try{ throw i }catch(i){ setTimeout( () => { console.log(i) }, 0); } } // 将 i 作为异常抛出,传递给 setTimeout