习题解答
- 基于promise的改造Ajax
- JS实现bind方法
- JS实现call/apply方法
- JS的闭包 & 作用域
- 实现一个instanceof
- 手写实现去除字符串连续重复值(例:'aaabcdeefghhb'=>'abcdefghb')
- num++ 和 ++num
- 函数执行 例:fn${num}模板字符串
- continue & break & return
- 下面这些值哪些是假值?
- typeof typeof 1
- [..."hzf"]返回什么
- 小程序里面canvas map等组件优先级最高,怎么将其覆盖
- Promise 中断或者取消
- JavaScript中Null和Undefined的区别
- 堆内存
- 作用域
- 原型链
- import导入不可被修改
- JSON.stringify参数
- String.raw
# 基于promise的改造Ajax
function ajax(url, callback,error) {
$.ajax({
url: url,
type: 'get',
success: function (data) {
callback(data);
},
error: function (err) {
error(err)
console.log('error');
}
})
}
function request(url) {
return new Promise((resolve, reject)=> {
ajax(url, resolve, reject);
})
}
//使用方式
request('a.txt').then( (res)=> {
if (res === 0) {
console.log(res);
return request('b.txt')
}
}).then(function (res2) {
if (res2 === 1) {
console.log(res2);
return request('c.txt');
}
}).then(function (res3) {
console.log(res3);
})
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
# JS实现bind方法
Function.prototype.bindFn = function bind(thisArg){
if(typeof this !== 'function'){
throw new TypeError(this + 'must be a function');
}
// 存储函数本身
const self = this;
// 去除thisArg的其他参数 转成数组
let args = [].slice.call(arguments, 1); //获取所有的形参,但是要剔除第一个,因为thisArg就是第一个
let bound = function(){
// bind返回的函数 的参数转成数组
let boundArgs = [].slice.call(arguments);
// apply修改this指向,把两个函数的参数合并传给self函数,并执行self函数,返回执行结果
return self.apply(thisArg, args.concat(boundArgs));
}
return bound;
}
// 测试
let obj = {
name: 'hzf',
};
function original(a, b){
console.log(this.name);
console.log([a, b]);
}
let bound = original.bindFn(obj, 1);
bound(2); // 'hzf', [1, 2]
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
# JS实现call/apply方法
Function.prototype.myCall = function(thisArg) {
// this指向调用call的对象
if (typeof this !== 'function') {
throw new TypeError('Error');
}
// 声明一个 Symbol 属性,防止 fn 被占用
const fn = Symbol('fn');
const args = [].slice.call(arguments,1); // 若是封装apply函数,const args = [].slice.call(arguments,1,2);
thisArg = thisArg || window;
// 将调用call函数的对象添加到thisArg的属性中
thisArg[fn] = this;
// 执行该属性
const result = thisArg[fn](args);
// 删除该属性
delete thisArg[fn];
// 返回函数执行结果
return result;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# JS的闭包 & 作用域
闭包的概念
- 一个函数执行完后所在的作用域没有被销毁,就形成了闭包
- 闭包就是能够读取其他函数内部变量的函数。
最大用处有两个
- 可以读取自身函数外部的变量(沿着作用域链寻找)
- 让这些变量的值始终保持在内存中
//变量保存到内存中
function f1(){
var n = 999;
nAdd = function(){n+=1} //nAdd是一个全局变量
function f2 (){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
//n一直保存在内存中,为什么没有在f1调用后被自动清除?
//原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用闭包注意的点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大
,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露
。解决方法是,在退出函数之前,将不使用的局部变量全部删除
。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便
看题
let obj1={
name:"aaa",
print:function(){
return ()=>console.log(this.name)
}
}
let obj2={name:"bbb"}
obj1.print()() //aaa
obj1.print().call(obj2) //aaa bind不能改变箭头函数的this指向
obj1.print.call(obj2)() //bbb
2
3
4
5
6
7
8
9
10
11
作用域
function test(){
var arr = [];
for(var i = 0;i < 10;i++){
//js函数内的变量值不是在编译的时候就确定的,而是等在运行时期再去寻找的
arr[i] = function(){
return i //函数内部并没有声明这i,所以就会沿着作用域链去找i变量,结果在上一级找到变量i
};
}
for(var a = 0;a < 10;a++){
console.log(arr[a]());
}
}
test(); // 连续打印 10 个 10
//声明块作用域
function test(){
var arr = [];
for(let i = 0;i < 10;i++){ // 仅在这里作出了改动
arr[i] = function(){
return i;
};
}
for(var a = 0;a < 10;a++){
console.log(arr[a]());
}
}
test(); // 打印 0 到 9
function test(){
var arr = [];
for(var i = 0;i < 10;i++){
//闭包
((i)=>arr[i] = function(){
return i;
})(i);
}
for(let a = 0;a < 10;a++){
console.log(arr[a]());
}
}
test(); // 打印 0 到 9
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
# 实现一个instanceof
先看用法
[1, 2, 3] instanceof Array; //true
//可以看到[1, 2, 3]是类型Array的实例
[1, 2, 3] instanceof Object; //true
2
3
实现一个instanceof
function myInstanceof(val, type) {
let rightProto = type.prototype; // 取右边 prototype的值
let leftPrevProto = val.__proto__; // 取左边__proto__值
while (true) {
if (leftPrevProto === null) { //如果左边的__proto__值为null,返回false
return false;
}
if (leftPrevProto === rightProto) {
return true;
}
leftPrevProto = leftPrevProto.__proto__ ; //以上都不满足,取上一层原型继续循环,直到没取到为null
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 手写实现去除字符串连续重复值(例:'aaabcdeefghhb'=>'abcdefghb')
//方法一
function removeRepeatStr(str){
let newStr = '';
for (let item of str) {
console.log(item);
newStr.indexOf(item) === -1 && (newStr = newStr + item);
// !newStr.includes(item) && (newStr = newStr + item)
}
return newStr
};
//方法二 利用对象
function removeRepeatStr(str){
let obj = {};
let newStr = '';
for (let item of str) {
if (!obj[item]) {
obj[item] = item;
newStr += item
}
}
return newStr
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# num++ 和 ++num
let number = 0;
console.log(number++);
console.log(++number);
console.log(number);
2
3
4
后缀一元运算符++:先返回在增加
前缀一元运算符++:先增加在返回
# 函数执行 例:fn${num}模板字符串
function getPersonInfo(one, two, three) {
console.log(one);
console.log(two);
console.log(three);
}
const person = "Lydia";
const age = 21;
getPersonInfo`${person} is ${age} years old`;
//第一个参数的值始终是字符串值的数组
//余参数获取传递到模板字符串中的表达式的值!
2
3
4
5
6
7
8
9
10
11
12
# continue & break & return
break用于完全结束一个循环,跳出循环体执行循环后面的语句,完全终止循环。
continue只是终止本次循环,接着还执行后面的循环
for (let i = 1; i < 5; i++) {
if (i === 3) continue;
console.log(i);
}
//1,2,4
for (let i = 1; i < 5; i++) {
if (i === 3) break;
console.log(i);
}
//1,2
for (let i = 1; i < 5; i++) {
if (i === 3) return true;
console.log(i);
}
//1,2 true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 下面这些值哪些是假值?
0;
new Number(0);
("");
(" ");
new Boolean(false);
undefined;
//JavaScript中只有6个假值:
undefined
null
NaN
0
'' (empty string)
false
2
3
4
5
6
7
8
9
10
11
12
13
14
# typeof typeof 1
typeof 1 //返回 "number".
typeof "number" //返回 "string"
2
# [..."hzf"]返回什么
["h", "z", "f"]
# 小程序里面canvas map等组件优先级最高,怎么将其覆盖
可以在state里定义了一个radarImg,然后在render中判断,radarImg这个值是否有效,若有效,canvas隐藏;否则,显示canvas。然后在页面渲染雷达图时,执行了Taro.canvasToTempfilepath方法,将雷达图转化为图片。此时给radarImg赋值,canvas隐藏,image显示
Taro.canvasToTempFilePath({
x: 0,
y: 0,
width: 260,
height: 180,
canvasId: 'radarCanvas',//画布标识,传入 <Canvas> 组件的 canvasId
success: function({tempFilePath}) {
that.setState({ radarImg:tempFilePath});
}
});
2
3
4
5
6
7
8
9
10
canvasToTempfilepath (opens new window)
# Promise 中断或者取消
Promise.race()
let promise1 = new Promise(function(resolve, reject) {
//模拟ajax异步请求
setTimeout(resolve, 3000, '接口返回成功!');
});
let promise2 = new Promise(function(resolve, reject) {
document.querySelector('#cancel').addEventListener('click', function() {
reject('取消等待接口!');
});
});
Promise.race([promise1, promise2]).then(function(value) {
console.log(value);
}).catch(function(value) {
console.log(value);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
直接抛出一个错误
throw new error()
// or
return Promise.reject()
2
3
保持pedding状态
let promise = new Promise(function(resolve, reject){
resolve('第一次成功')
})
promise.then(function(val) {
return new Promise(()=>{})
}).then(function(val) {
console.log('被跳过的方法')
}).catch(function(val) {
console.log('返回失败')
})
2
3
4
5
6
7
8
9
10
# JavaScript中Null和Undefined的区别
Null
null是js中的关键字,表示空值,null可以看作是object的一个特殊的值,如果一个object值为空,表示这个对象不是有效对象。
Undefined
undefined不是js中的关键字,其是一个全局变量,是Global的一个属性,以下情况会返回undefined:
1)使用了一个未定义的变量;var i;
2)使用了已定义但未声明的变量;
3)使用了一个对象属性,但该属性不存在或者未赋值;
4) 调用函数时,该提供的参数没有提供:
function func(a){
console.log(a);
}
func();//undefined
2
3
4
5)函数没有返回值时,默认返回undefined
let aa=func();
aa;//undefined
2
相同点:
都是原始类型的值,保存在栈中变量本地
两者的区别:
1.类型不一样:
console.log(typeOf undefined);//undefined
console.log(typeOf null);//object
2
2.转化为值时不一样:undefined为NaN ,null为0
console.log(Number(undefined));//NaN
console.log(Number(10+undefined));//NaN
console.log(Number(null));//0
console.log(Number(10+null));//10
2
3
4
5
undefined===null;//false
undefined==null;//true
2
何时使用:
null当使用完一个比较大的对象时,需要对其进行释放内存时,设置为null;
var arr=["aa","bb","cc"];
arr=null;//释放指向数组的引用
# 堆内存
const getInfo=(member,year)=>{
member.name ="大江";
year=21
}
let person ={name:"和振峰"}
let age=23
getInfo(person,age)
console.log(person,age) //{name: "大江"} 23
2
3
4
5
6
7
8
9
10
# 作用域
(()=>{
let x = (y =1008611)
})()
console.log(typeof x , typeof y)
2
3
4
# 原型链
class person {
name='和振峰'
static age=22
}
person.sex=1
person.prototype.city='邯郸'
let l = new person()
console.log(l) //如下图
person.age //22
person.sex //1
l.__proto__.constructor.age //22
l.constructor.age //22
person.name //person
l.name //和振峰
delete l.name
l.name //undefined
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# import导入不可被修改
// counter.js
let counter =10;
export default counter
// index.js
import counter from "./counter"
counter+=1
console.log(counter) //不可被修改,只读的
2
3
4
5
6
7
8
9
10
引用数据类型是可修改的,慎重慎重
# JSON.stringify参数
let obj={a:1,b:2}
JSON.stringify(obj,['a']) //"{"a":1}"
2
# String.raw
const path =`C:\Documents\Projects\table.html`
// \为转义符,会对结果有影响
path //"C:DocumentsProjects able.html"
String.raw`${path}` //"C:DocumentsProjects able.html"
String.raw`C:\Documents\Projects\table.html` //"C:\Documents\Projects\table.html"
//可以用正则全局匹配下 '\' replace '\\'
const draftPath =`C:\\Documents\\Projects\\table.html`
draftPath //"C:\Documents\Projects\table.html"
2
3
4
5
6
7
8
9
← 配置WS