习题解答

# 基于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);
})

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

# 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]

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

# 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;
}
1
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也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
1
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
1
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
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

# 实现一个instanceof

先看用法

[1, 2, 3] instanceof Array; //true
//可以看到[1, 2, 3]是类型Array的实例
[1, 2, 3] instanceof Object; //true
1
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
    }
}
1
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
};
1
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);
1
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`;
//第一个参数的值始终是字符串值的数组
//余参数获取传递到模板字符串中的表达式的值!
1
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
1
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# typeof typeof 1

typeof 1  //返回 "number".
typeof "number" //返回 "string"
1
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});
    }
});
1
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);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

直接抛出一个错误

throw new error()
// or
return Promise.reject()
1
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('返回失败')
})
1
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
1
2
3
4

5)函数没有返回值时,默认返回undefined

let aa=func();
aa;//undefined
1
2

相同点:

都是原始类型的值,保存在栈中变量本地

两者的区别:

1.类型不一样:

console.log(typeOf undefined);//undefined
console.log(typeOf null);//object
1
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
1
2
3
4
5
undefined===null;//false
undefined==null;//true
1
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
1
2
3
4
5
6
7
8
9
10

# 作用域

(()=>{
 let x = (y =1008611)
})()
console.log(typeof x , typeof y)
1
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
1
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)   //不可被修改,只读的
1
2
3
4
5
6
7
8
9
10

引用数据类型是可修改的,慎重慎重

# JSON.stringify参数

let obj={a:1,b:2}
JSON.stringify(obj,['a']) //"{"a":1}"
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"
1
2
3
4
5
6
7
8
9