JS汇总1

文章目录

  • 函数汇总
    • 操作数组方法
      • sort()
      • 字符串变数组
      • 数组变字符串
      • 数组变对象
      • 对象变数组
      • 对象添加属性
      • 复制对象
  • Map和Set
    • 正则表达式
    • 正则表达式组合
    • 合并数组
      • isNaN()
      • isFinite()
      • Boolean()
    • 原型
      • obj.hasOwnProperty(prop)
    • 进制转换
    • 数值转换
      • instanceof
      • Number() pareseInt() parsefloat()
      • toString()
      • toUpperCase() toLowerCase()
      • Object.defineProperty()
      • Math.pow()
      • JSON.parse JSON.stringity
  • HTML DOM Event 对象
  • 判断类型
  • js错误类型
  • JS基础
      • for in key
    • 1.概述
    • 2.规范
    • 3.数据类型
      • 1.字符串
      • 2.数组
      • 3.严格检查模式
      • 4.对象
      • 5.流程控制
      • 6.Map Set
    • 判断数组
    • 4.函数及面对对象
      • 1.定义函数
      • 2.变量的作用域
  • 预编译
  • 作用域与闭包
    • JS的作用域
      • 作用域的概念
    • 闭包
      • 对代码的作用域分析
      • 单例模式
      • 理解含义
      • 四种闭包书写方式(倾向于第四种)
      • 闭包有什么好处呢?
  • js中的super
    • 1.this和super的区别:
          • this关键词指向函数所在的当前对象
          • super指向的是当前对象的原型对象
    • 2.super的简单应用
    • 3.super的另类实现
    • 4.super中的this指向(易混淆)
  • 防抖与节流函数
    • 防抖
    • 代码分析
    • 节流
  • this
    • 箭头函数和this
      • 一、ES5中
      • 二、箭头函数的this
      • 三、apply、call、bind 函数
  • 赋值、深浅拷贝
    • 浅拷贝 与 赋值的区别
  • 数组扁平化
  • arguments对象
  • promise
      • 回调函数分类
      • callback
      • promise是什么?
      • 优势
      • 步骤
    • 错误处理
      • 错误处理两种做法:
    • Promise.all() 批量执行
    • Promise.race()
      • 封装对象
      • 若干问题
    • 手写promise
    • async/await yield
  • ...展开运算符
  • ajax和axios
    • ajax
    • 如何解决跨域问题? jsonp
    • axios
  • ES6
    • 不同
      • 箭头函数
    • let var const
    • forEach、for in 、 for of三者的区别
  • 原型链
  • https://juejin.cn/post/6844903885488783374
  • Generator函数
  • BFC概念
  • call、apply、bind
    • 区别
    • 实现
      • call
      • apply
      • bind
  • symbol
  • 定时器滚动图片
  • this与原型链题目
  • JS事件循环机制
  • 设计模式
    • 单例模式
    • 发布订阅模式
    • 工厂模式
  • 四大编程思想
  • H5C3
  • 事件流
  • 事件委托
  • 继承
  • 宏任务 微任务 同步队列
  • 构造函数静态,实例成员
  • 栈堆,数组链表

函数汇总

操作数组方法

一、改变原始数组的方法:

1、pop()

删除 arrayObject 的最后一个元素,把数组长度减 1,并且返回它删除的元素的值。如果数组已经为空,则 pop() 不 改变数组,并返回 undefined 值。arrayObject.pop() 。

2、push()

push() 方法可把它的参数顺序添加到 arrayObject 的尾部。它直接修改 arrayObject,而不是创建一个新的数组,arrayObject.push(newelement1,newelement2,….,newelementX) 。

3、reverse()

该方法会改变原来的数组----将原来的数组倒序,而不会创建新的数组。arrayObject.reverse()。

4、shift()

删除数组的第一个元素,并返回第一个元素的值,如果数组是空的,那么 shift() 方法将不进行任何操作。

5、unshift()

unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。arrayObject.unshift(newelement1,newelement2,….,newelementX)返回arrayObject 的新长度。

6、sort()

对数组的引用。请注意,数组在原数组上进行排序,不生成副本。arrayObject.sort(sortby) (如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。
  如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:

若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
若 a 等于 b,则返回 0。
若 a 大于 b,则返回一个大于 0 的值。)

排序则需要:比较函数

let arr = input.sort((a,b) => a-b)

7、splice()

splice() 方法可删除从 index 处开始的零个或多个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素。 如果从 arrayObject 中删除了元素,则返回的是含有被删除的元素的数组 arrayObject.splice(index,howmany,item1,……,itemX) 。

splice(2,1,‘www’) 从index==2删除一个,加上一个

8、split()

9、pop

​ 删除最后一个元素

二、不改变原始数组的方法:

1、concat()

用于连接两个或多个数组,仅会返回被连接数组的一个副本,arrayObject.concat(arrayX,arrayX,……,arrayX) 。

2、join()

返回一个字符串。该字符串是通过把 arrayObject 的每个元素转换为字符串,然后把这些字符串连接起来,arrayObject.join(separator) 。

3、slice()

arrayObject.slice(start,end)返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。

slice(2,4) 截取index2到index4-1=3

4、JSON.parse(JSON.stringify(arry))

这种方式会重新复制一个数组。也是实现深拷贝的一种方式。

join() 方法用于把数组中的所有元素放入一个字符串,通过指定的分隔符进行分隔的。

split() 方法用于把一个字符串分割成字符串数组。

splice() start,end 删除

slice() 方法可从已有的数组中返回选定的元素。 不会改变原数组 slice(start,end)

push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。

indexOf() 返回匹配的第一个元素的下标

pop() 用于删除并返回数组的最后一个元素。

find()函数用来查找目标元素,找到就返回该元素,找不到返回undefined。

findIndex()函数也是查找目标元素,找到就返回元素的位置,找不到就返回-1。

他们的都是一个查找回调函数。

sort()

https://www.cnblogs.com/bigbang66/p/13660994.html

注意: 一切都是对数组操作的,比如数组对象

默认排序顺序是根据字符串UniCode码。所以首先应该把数组元素都转化成字符串(如有必要),以便进行比较。

一、概念

arr.sort((a,b) => {
    return a - b     //升序
}) 
比较函数应该具有两个参数 a 和 b,其返回值如下:
若 a 小于 b,即 a - b 小于零,则返回一个小于零的值,数组将按照升序排列。
若 a 等于 b,则返回 0。
若 a 大于 b, 即 a - b 大于零,则返回一个大于零的值,数组将按照降序排列。

二、对字符串

数字
*********************
let arr = [2,4,8,7,3,6]
arr.sort          //[2, 3, 4, 6, 7, 8]


字符串
**********************
let arr = ['cv','s','av','ac','dfs']
arr.sort()         //["ac", "av", "cv", "dfs", "s"]
arr.sort((a,b) => {
  return a.localeCompare(b)
})              //["ac", "av", "cv", "dfs", "s"]
使用字符串的 localeCompare() 方法,可以根据本地约定顺序来比较两个字符串的大小。


字符串数字
***********************
let arr = ['10','200','30']
arr.sort((a,b) => {return a - b})   //["10", "30", "200"]

["2", "20", "23", "4", "8"] =>   8423220 最大

b.sort((a,b) => {
      return (b+a) - (a+b)
    })



数组对象
***********************

    
    
 
对象
************************
新方法:
Object.keys() Object.vaules()
[...map.values()]) [...map.keys()]
    

  因为对象只有一串属性key和一串值value
所以对它进行操作实际上可以将它们分开处理
例如: afaafbbrbc'     变为'aaabbbffcr'
1、大小升序排序 2、个数升序排序

用到了两次sort()
步骤:
1、将数组写入对象,个数+1
2、通过Object.keys()将对象中的keys取出为数组 ["a", "f", "b", "r", "c"]
3、将数组升序排序
4、将数组对应的对象属性个数升序排序
5、两次循环打印对象
let str = 'afaafbbrbc';
  function fineE(str) {
    let obj = {};
    let res = [];
    for(let i = 0; i < str.length; i++) {
      obj[str[i]] ?  obj[str[i]] = obj[str[i]] + 1 :obj[str[i]] = 1
    }
    let line = Object.keys(obj)
    console.log(line);  //["r", "f", "c", "b", "a"]
    line.sort((a,b) => {    
      return a.localeCompare(b)
    })
    console.log(line);  //["r", "f", "c", "b", "a"]
    line.sort((a,b) => {
      return obj[b] - obj[a]
    })
    console.log(line);   //["a", "b", "f", "c", "r"]
    for(let i = 0; i < line.length; i++) {
      for(let j = 0; j < obj[line[i]]; j++) {
        res.push(line[i])
      }
    }
    console.log(res);
    res = res.join('')
    console.log(res);
  }

reduce() 累加器,函数为参

let arr1 = [1,2,3,4,5]
    function add1(arr) {
        return arr.reduce((a,b) => {
        return a+b
      })
    }
    console.log(add1(arr1)); //15
let arr1 = [1,2,3,4,5]
    function add1(arr) {
        return arr.reduce((a,b) => {
        return a+b
      },8)
    }
    console.log(add1(arr1)); //23

function count(arr, item) {
            var count = arr.reduce(function(prev, curr) {
                return curr === item ? prev+1 : prev;
            }, 0);
            return count;
        }//第一个参数为结果

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

let arr1 = [1,2,3,4,5]
    function add1(arr) {
        return arr.filter((a) => {
        return a>3
      })
    }
    console.log(add1(arr1)); //4,5

字符串变数组

let arr = 'gehe'
arr.split('')  //[]
//将输入数组分开 [2,20,23,4,8] -> [2,[2,0],[2,3],4,8]
for(let i=0;i<a.length;i++) {
    arr.push((a[i].toString().split('')).map(Number))   
  }

数组变字符串

  let arr = [1,2,4,3,4]
  //方法一
arr.join()      //1,2,4,3,4
arr.join('')     //12434
	//方法二
arr.toString()   //1,2,4,3,4
  var arr = [1,[2,3],[4,5], [6,[7,8]]];
arr.toString()   //1,2,3,4,5,6,7,8

数组变对象

一、数组中分割成对象

b = ['key=2','key=3','test=4']

//直接添加方括号
b //['key=2','key=3','test=4']   //针对对象包含数组
 b.forEach(item => {
        let keys = item.split('=')[0]
        let val = item.split('=')[1]
        res[keys]? res[keys] = [].concat(res[keys],val) :res[keys] = val
      })
res //{'key':['2','3'],'test':'4'}

res[keys] = [res[keys], val]   //法1: 中括号[] ,放数组4
res[keys] = [].concat(res[keys],val)  //法2: [],concat填加数组

二、纯数组变对象

Object.assign(...arr1),{...arr}

let arr = [1,2,4,3,4,'d']
{...arr}  //{0: 1, 1: 2, 2: 4, 3: 3, 4: 4, 5: "d"}



let arr1=[
                     {user:"123"},
                    {Cause:"22"},
                    {EnterFactoryTime:"33"},
                    {OutFactoryTime:"44"},
                    {VehicleGrade:"55"},
                    {IncomingInspection:"66"},
                    {Admission:"77"}
             ];

let arr2 = Object.assign(...arr1)

//{user: "123", Cause: "22", EnterFactoryTime: "33", OutFactoryTime: "44", VehicleGrade: "55",....}

对象变数组

  let obj = {
    'a' : 1,
    'b' :2
  }
Object.keys(obj)       //["a", "b"]
Object.values(obj)      //[1, 2]


const map  = new Map()
map.set('a', 1)
map.set('b', 2)
[...map]             //[['a',1], ['b',2]]
Array.from(map)      //[['a',1], ['b',2]]
[...map.keys()];    //["a", "b"]
[...map.values()]      //[1, 2]

对象添加属性

//数组添加属性方法: 法一: (c是常量,直接在对象后加. ,对象.常量) bb = {}, bb.c = 1  bb输出: {c: 1}
//  法二: (a为变量,常用于循环) bb = {},  let a = 'ff', bb[a] = 1,   bb输出: {ff: 1}   

let bb = {}
    bb.q = 5
    let t = "fu"
    bb[t] = 55
    console.log(bb); //{q: 5, fu: 55}
    console.log(bb.q+1, ++bb[t]);     //6 56

复制对象

实际上就是对对象进行拷贝

浅拷贝:

obj1 = obj

obj1 = Object.assign(obj)

for(let i in obj) {
    obj1[i] = obj[i]
  }

深拷贝:

类型

git

less sass

== ===

字符串

  obj1 = JSON.parse(JSON.stringify(obj))

递归方法
function g(obj) {
    let target = {}
    if(typeof obj !== 'object') return obj
    for(let i in obj) {
        if(obj.hasOwnProperty(i)) {
            target[i] === g(obj[i])
        }
    }
    return target
}


Map和Set

Map

Map对象保存键值对,并且能够记住键的原始插入顺序。[key,value]的数组

const map  = new Map()
map.set('a', 1)
map.set('b', 2)
console.log(map.get('a'))      //1
console.log([...map.keys()]);     //["a", "b"]
console.log([...map.values()]);    //[1,2]
console.log([...map.entries()]);	//[Array(2), Array(2)]
console.log(Array.from(map));		//[Array(2), Array(2)]

let first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);
let second = new Map([
  [1, 'uno'],
  [2, 'dos']
]);
let man = new Map([...first, ...second])   //合并
console.log([...man]);       //[Array(2), Array(2), Array(2)]

for (let [key, value] of myMap.entries()) {
  console.log(key + " = " + value);
}

正则表达式

https://tool.oschina.net/uploads/apidocs/jquery/regexp.html

exec() 用于检索字符串中的正则表达式的匹配。只返回第一个值。

let a = 'rgb(255, 255, 255)'   
let reg = /\d+/g
let res = reg.exec(a)
console.log(res);
///
let a = 'rgb(255, 254, 255)'   
let reg = /(\d+)/g
let res
while ((res = reg.exec(a)) != null) {
  console.log(res[1]);
}

replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

match() 使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回

let a = 'rgb(255, 255, 255)'   
let res = a.match(/\d+/g) // ["255", "255", "255"] 返回所有值

test () 方法检查字符串是否与给出的正则表达式模式相匹配,如果是则返回 true,否则就返回 false。

语法:regexp.test(str)
手机号验证:!/^[1][3,4,5,7,8][0-9]{9}$/.test(this.phone)

正则表达式组合

用exec方法,因为它返回一个值,所以设置需要的模板数量,以[1],[2]来调用结果。

注意:输出时用模板字符串形式!!

模板: 用圆括号包裹模板

let arr = "functionFunction(555),(444)"
let name = /\((\w+)\),\((\w+)\)/g  //两个模板
let arr1 = name.exec(arr2)
console.log(arr1); //["(555),(444)", "555", "444", index: 16, input: "functionFunction(555),(444)", groups: undefined]
console.log(`${arr1[1]} ${arr1[2]}`)  //555 444

多行处理: /mg 换行+全局 ,模板字符串支持换行,对行数进行循环

let arr = `functionFunction(555),(444)
functionFunction(666),(333)`
let name = /\((\w+)\),\((\w+)\)/mg  //两个模板
let arr1 = name.exec(arr2)
console.log(arr1); 
do {
    console.log(`${arr1[1]} ${arr1[2]}`)
} while ((arr1 = name.exec(arr)) !== null)  //555 444
    										//666 333

使用命名组: 相当于添加属性

/(?.+)/mg arr1.groups.name

let arr = `functionFunction(555),(444)
functionFunction(666),(333)`
let name = /\((?.+)\),\((?.+)\)/mg
let arr1 = name.exec(arr2)
console.log(arr1); 
do {
    console.log(`${arr1.groups.first} ${arr1.groups.last}`)
} while ((arr1 = name.exec(arr)) !== null)  //555 444
    										//666 333

合并数组

// 合并
      let arr11 = [1,2,3]
      let arr22 = [4,5,6]
      let arr33 = "fsgdsfg/sgsdg/jtjt"
      //1
      console.log([...arr11,...arr22]);

     // 2
      console.log(arr11.concat(arr22));
      //3
      arr11.splice(3,1,arr22)
      console.log(arr11);
      //4
      for(let i in arr22) {
        arr11.push(i)
      }
      console.log(arr11);     //[1, 2, 3, "0", "1", "2"]

     // 5
      arr11.push(...arr22)     //...arr22、forEach、for 遍历


      //6
      arr22.forEach(i => {
        arr11.push(i)
        console.log(i);
      })
      console.log(arr11);

        arr33.split('/')  //切分
        arr11.join('/')   //加入

        //7
      Array.prototype.push.apply(arr11,arr22)          //依次push
        console.log(arr11);
      console.log(Array.prototype.slice.call(arr33));

isNaN()

测试不属于NaN数值的值

console.log(isNaN(10)); //false

isFinite()

判断一个值是不是在有限大与有限小之间。

console.log(isFinite(resule));  

Boolean()

将其它类型值转换为布尔类型,如空,null,undefined等为false

let b = "hello"
let a = Boolean(b); //ture

原型

obj.hasOwnProperty(prop)

obj.hasOwnProperty(prop) 方法功能就是判断obj上有没有prop这个属性
注意:此属性是自身属性,原型链上的属性不属于此属性

function shallow(obj) {
      let target = {}
      for (let i in obj) {
        if(obj.hasOwnProperty(i))  {
          target[i] = obj[i]
        }
      }
      return target
    }

进制转换

parseInt(): 字符串str按照radix进制编码方式转换为10进制返回,没有radix,默认为10;

  let res = parseInt(str, 2) //str='1100000' 输出: 192

toString(): 返回表示该数字的指定进制形式的字符串。(把10进制的数据转为指定进制,并以字符串形式输出);radix支持 [2, 36] 之间的整数。默认为10;

 let x = 10; x.toString(2) // 1010

数值转换

p36–第四版

instanceof

obj instanceof Date 
    意思是: Date的propertypeof是否在obj的原型链上

Number() pareseInt() parsefloat()

toString()

1.返回自身一个副本

let lang = "java";
console.log(lang.toString())  //java  

2.返回当前字符等价物

let age = 11;
let age1 = age.toString(); //11 

3.参数进制数的字符串

let num = 10;
console.log(num.toString(2)); //"1010"

toUpperCase() toLowerCase()

把字符串转换为大小写

var txt="Runoob";
document.write(txt.toLowerCase() + "
"
); //runoob document.write(txt.toUpperCase()); //RUNOOB

Object.defineProperty()

使用符号作为属性

let s1 = Symbol('foo'),
    s2 = Symbol('goo'),
    s3 = Symbol('hoo'),
    s4 = Symbol('joo');

let o = {[s1]: 'foo val'};
console.log(o);   //{Symbol(foo): "foo val"}

Object.defineProperties(o,{
    [s3]:{value:'hoo val'},
    [s4]:{value:'joo val'}
});
console.log(o);//{Symbol(foo): "foo val", Symbol(hoo): "hoo val", Symbol(joo): "joo val"}

Math.pow()

指数操作

console.log(Math.path(3,2))//9
let s = 3;
s ** = 2;
console.log(s); //9

JSON.parse JSON.stringity

JSON.stringify`会处理的几种类型: `String, Number, Boolean, null, Array, Object`
不会处理的几种类型: `Date, RegExp, undefined, Function
	<script type="text/javascript">
		//序列化,js对象转换成json字符串
		var obj={'name':'tim','age':16}
		console.log(obj,typeof(obj))
		console.log(JSON.stringify(obj),typeof(JSON.stringify(obj)))

		//反序列化   json字符串转换成js对象
		console.log('********反序列化*********')
		var jsonString='{"name":"tim","age":18}'
		console.log(jsonString,typeof(jsonString))
		console.log(JSON.parse(jsonString),typeof(JSON.parse(jsonString)))




	</script>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CtHHkwW2-1632368985381)(JS.assets/image-20210221155333401.png)]

HTML DOM Event 对象

event.preventDefault(); 阻止默认的点击事件执行 ,一般用于按钮,对按钮事件进行重新编辑,比如默认为提交数据,需求则是在数据添加警告,则设计方法

$('#register_form').on('submit', function (e) {
      e.preventDefault() //阻止默认动作
      var formData = $(this).serialize()  //通过序列化表单值,创建 URL 编码文本字符串, eamil=fewfew&nickname=fewgwewg
      $.ajax({
        url: '/register',
        type: 'post',
        data: formData,
        dataType: 'json',
        success: function (data) {
          var err_code = data.err_code
          if (err_code === 0) {
            // window.alert('注册成功!')
            // 服务端重定向针对异步请求无效
            window.location.href = '/'
          } else if (err_code === 1) {
            window.alert('邮箱已存在!')
          } else if (err_code === 2) {
            window.alert('昵称已存在!')
          } else if (err_code === 500) {
            window.alert('服务器忙,请稍后重试!')
          }
        }
      })
    })

判断类型

https://zhuanlan.zhihu.com/p/89238840

typeof

Object.prototype.toString.call()

obj instanceof Object

Array.prototype.isPrototypeOf()

js错误类型

https://www.cnblogs.com/yanze/p/5997489.html

1.SyntaxError(语法错误)

解析代码时发生的语法错误

eg:var 1a;

Uncaught SyntaxError: Unexpected number

2.ReferenceError(引用错误)

a.引用了一个不存在的变量

eg: console.log(a);

Uncaught ReferenceError: a is not defined

b.将变量赋值给一个无法被赋值的对象

eg:console.log()= 1;

Uncaught ReferenceError: Invalid left-hand side in assignment

3.RangeError(范围错误)

超出有效范围

eg:var a= new Array(-1);

Uncaught RangeError: Invalid array length

4.TypeError(类型错误)

a.变量或参数不是预期类型,比如,对字符串、布尔值、数值等原始类型的值使用new命令,就会抛出这种错误,因为new命令的参数应该是一个构造函数。

eg: var a= new 123;

Uncaught TypeError: 123 is not a function

b.调用对象不存在的方法

eg:var a;a.aa();

Uncaught TypeError: Cannot read property ‘aa’ of undefined

5.URLError(URL错误)

与url相关函数参数不正确,主要是encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()这六个函数。

eg: decodeURI(’%2’)

Uncaught URIError: URI malformed

6.EvalError(eval错误)

eval函数没有被正确执行

JS基础

for in key

	function aa(){
		let def = new Array();
		for(let i =1;i<6;i++){
			let k = i+6;
			def[k] = i+1;
		}
		for(let b in def){
			console.log(b);
		}
        for(let b in def){
			console.log(def[b]);
		}
	}//7,8,9,10,11 //2,3,4,5,6

for循环之外定义def数值属于全局变量。 而k则是局部变量,

所以for(let k in def)只是循环def中元素个数的数量。 重点在循环次数,不在循环内容

1.概述

https://blog.csdn.net/kese7952/article/details/79357868

ECMAScript 是javasctipt的一个标准

最新版本已经到6了

大部分浏览器支持es5

2.规范

script放在head 或者body

一定要双标签

多行备注/****/

严格区分大小写

console.log(num) 打印变量

source,分步操作

application 存放在浏览器

3.数据类型

number

Nan //not a number
Infinity //无限大

比较运算符

== 等于 (类型不同。值一样也为ture)
=== 绝对等于(类型一样,值一样 ture)
  • NaN===NaN 与所有的数值都不相等,包括自己
  • 只能通过isNaN(NaN) 为true

尽量避免浮点数,精度问题。

console.log(Math.abs(1/3-(1-2/3))<0.000001) // 确定精度可以

null和undefined

数组

js中数组内不需要相同类型对象。

var arr = [1,2,5,4,56,'ddd',null,ture]


数组越界,输出undefined

对象

对象大括号

var person = {
    name:"ddd",
    age:5,
    tags:['dd','ddd']
}

1.字符串

1.正常字符串 使用单引号或双引号包裹。

2.转义字符 ’a\‘’ 输出a‘

\'
\n 
\t 空格
\u4e2d 中  Unicode字符
\x41 Ascall字符

3.多行字符串编写

var msg = `  //tab键上面
    hello
    world
`

4.模板字符串

let name = "dsghj";
let mad = `你好,${name}`

5.获得属性某状态

console.log(name.length)

6.方法

//这里是方法,不是属性
console.log(name.toUpperCase()) //大小写转换
console.log(name.indexOf('d'))// 元素下标 0
console.log(name.substring(1)) //截取,sghj 
console.log(name.substring(13)) //sg 包括前不包括后 

2.数组

Array可以包含任意数据类型

1.长度可编辑,但是arr.length变小。内容则会丢失部分

2.slice()类似substring() 截取Array一部分

3.arr.push(‘a’,‘b’)压入元素 到最后面 arr.push() 踢出尾部元素

4.arr.unshift() 头部压入 shift()弹出头部

5.排序 sort() 可对字符排序(依据Ascall) 反转reverse()

6.concat(1,2,3)加入拼接输出,但是不对数组有影响

7.arr.join(’-’)
“1-1-2-A-B”
arr
(5) [1, 1, 2, “A”, “B”] 添加连接符

8.arr.splice(0,0,“排球”) 三个参数,[第几位,删去多少个数,添加元素]

arr.splice(1,2,"dd","aa")
		console.log(arr)
		console.log(arr.indexOf("1"))

3.严格检查模式

'use strict'// 写在script第一行
let i = 1;

预防js的随意性问题

局部变量使用let定义

4.对象

JavaScript中所有的键都是字符串,值是任意对象

1.动态的删减属性

delete person.age   //减
true

person.age = "hh"  //增加
"hh"

2.判断一个属性是否在对象中hasOwnProperty()

person.hasOwnProperty('age')
true

5.流程控制

1.数组遍历 for in /// for of

var arr = [1,2,5,6,3,6]

for(let num in arr){
    console.log(arr[num])
}
或者
for(let num of arr){
    console.log(num)
}

6.Map Set

Map:

var map = new Map([['ff',12],['dd',13],['ee',15]])
var named = map.get('ff')
console.log(named);
map.set('ggg',2)
console.log(map)

for(let num of map){    //遍历map
    console.log(num)
}

Set:无序不重复

var set = new Set([3,2,14,454,4,4,4,4]);
console.log(set)

1.js:36 Set(5) {3, 2, 14, 454, 4}

判断数组

https://www.cnblogs.com/lingdu87/p/9152806.html

4.函数及面对对象

1.定义函数

var abs = function(x){
    //收到抛出异常
    if(typeof x!=='number'){
        throw 'Not a Number'
    }
}

arguments

arguments:代表传递进来的所有参数x,是一个数组

var abs = function(x){
    //收到抛出异常
    if(typeof x!=='number'){
        throw 'Not a Number'
    }
        for(let i=0;i<arguments.length;i++){
        console.log(arguments[i]);
    }
}

rest

获取除了已经给定的参数外的所有参数, 只能写在后面

function aaa(a,b,...rest){
    console.log(a)
    console.log(b)
    console.log(rest)
}
aaa(1,2,5,4,6,36,245,56,6)
1
2
(7) [5, 4, 6, 36, 245, 56, 6]

2.变量的作用域

如果两个函数使用同一变量名,互不相干

let与var区别之一:var可以被作用域提升,即后面定义变量前面不会出错,输出undefined

const与let的区别:const声明变量使必须同时初始化变量,且不能修改。

let声明for循环变量,const声明for循环不被修改的变量。

**总结:**一般声明不用var

for (const key in {a:1,b:2}) {
    console.log(key);
    }//a b

for (const value of [1,2,3,4,5,6]) {
    console.log(value)
}// 1 2 3 4 5 6
function qj(){
    var x = 1;

    function qqj(){
        var x = 'a'
        console.log('1'+x)

    }
    console.log('2'+x)
    qqj()
}
qj()

21
1a

调用外部函数不会运行内部函数,调用内部变量,由内向外查找,会屏蔽外部变量,就近原则

提升变量的作用域

function ddh(){
    var y;
    var x='x'+y;
    y='y'
    console.log(x)
}
ddh()

结果:xundefined

说明:js执行引擎,自动提升了y的声明,但不会提升变量y的赋值;

所以需要养成规范:所有变量定义都放在函数头部,便于修改。

function ddh(){
    var y ='y',
        x = 'x'+y,
        z,x,y;//undefined
}

定义在类里面的则都可以使用

var x=55;
function asd(){
    console.log(x)
}
asd()
console.log(x+1)

55
56

全局对象window

alert(x)
alert(window.x)//默认所有全局变量 都会自动绑定window对象下
window.alert(x)

规范

当不同的js文件使用同一全局变量,就有冲突。所以在每个js文件定义不同全局变量,把所有代码放入自己定义的唯一变量内

//唯一全局变量
var person = { 
    name:"ddd",
    age:5,
    tags:['dd','ddd']
}
person.name = "ddfgg";

局部作用域

function asd(){
    for(let i=0;i<100;i++){
        console.log(i)
    }
    console.log(i+1) //Uncaught ReferenceError: i is not defined
}

如果是var i,则console.log(i+1) 能输出结果

预编译

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CvyNMu7z-1632368985388)(C:\Users\11490\AppData\Roaming\Typora\typora-user-images\image-20210517214909033.png)]

作用域与闭包

JS的作用域

作用域的概念

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aCPTDTJv-1632368985390)(JS.assets/image-20210421090304311.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S638Yr11-1632368985391)(JS.assets/image-20210421090428867.png)]

闭包

https://www.jianshu.com/p/9fc2e3ee4efe

闭包能够访问外部函数的变量,即使变量已经离开它所创建的环境,是因为外部变量会被闭包的作用域对象所持有。闭包这种特性实现了嵌套函数之间数据的隐式传递。

作用域链: 会被保存到一个隐式的属性中央[{scope}]这个属性是我们用户访问不到的,但是的确是存在的人,让js引擎来访问的, 里面存储的就是作用域链

一:预编译:js在运行的时候,会为每一个执行函数分配内存空间,该空间就是作用域对象(scope)。

二、执行:调用函数时,函数中的变量会依次存储到该作用域对象中。所以当内部函数用到需要外部变量时,就会在作用域链中向外找,如果找不到,那抛出错误referenceError。

闭包的缺点:

因为使用闭包,可以使函数在执行完后不被销毁,保留在内存中,如果大量使用闭包就会造成内存泄露,内存消耗很大

对代码的作用域分析

function a() {         //可以用节流和防抖函数来分析
    let aa = 345
    function b() {
    	let bb = 123
        aa = 0
    }
    return b
}
a()

一、定义a,放入作用域顶端,全局变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r905RADt-1632368985392)(JS.assets/2021-04-21_092828.png)]

二、执行a

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kSPSZowk-1632368985393)(JS.assets/2021-04-21_092903.png)]

三、定义b

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9cBybwm7-1632368985394)(JS.assets/2021-04-21_092918.png)]

四、执行b

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zo8MSNlm-1632368985395)(JS.assets/image-20210421103003825.png)]

五、当a执行完后,b定义的线依然存在,所以a执行的线被剪断后(a执行完后),b依然可以用到a执行时的变量,a=123

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6uizeOoT-1632368985396)(JS.assets/image-20210421103715492.png)]

单例模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D4yYfEko-1632368985397)(JS.assets/2021-04-21_173400.png)]

理解含义

js没有c++中class的public和private的区分(ES5中),只有全局变量和局部变量这两种,引入闭包就使得js有了私有变量这一概念。
下面给出闭包的定义:闭包 是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量。调用完一次后,函数没有被释放,整条作用域链上的局部变量都将得到保留。
在本质上,闭包就是将函数内部和函数外部连接的一座桥梁

四种闭包书写方式(倾向于第四种)

function a(){        //   闭包方式一    
			var tim=5
			return function(){
				console.log(tim)
			}
			
		}
		
		a()();
		
		
		function a(){        //   闭包方式二
			var tim=5
			var b=function(){
				console.log(tim)
			}
			return b
		}
		
		var b=a()
		b()
		
		
		var a=(function (){        //   闭包方式三
			var tim=9
			var b=function(){
				console.log(tim)
			}
			return b
		})();
		
		a()
		
		
		var a=(function (){        //   闭包方式三_1,返回内部函数
			var tim=9
			return function(){
				console.log(tim)
			}
			
		})();
		
		a()       //这种比较好,原因是有() 提醒这是闭包,然后调用只需要一个括号,如a()

//输出5.5.9.9

与平时多一个调用函数步骤

function rec(){            //平时
	let dd=4;
	return dd;
};
let le =rec();		//声明le为rec(),也可以不声明,直接alert(rec())
alert(le)              //4
function rec(){           //闭包
	let dd=4;
	return function(){
		return dd;
	};
	};

// let le =rec()
alert(le()) 
alert(rec()())         //4
rec()()                //返回空,其实是返回的,只是没有console.log输出4

闭包有什么好处呢?

1.希望一个变量长期驻扎在内存中

2.避免全局变量的污染

3.私有成员的存在

我们使用全局变量也可以实现同样的效果

js中的super

1.this和super的区别:

  • this关键词指向函数所在的当前对象
  • super指向的是当前对象的原型对象

2.super的简单应用

[复制代码](javascript:void(0)

const person = {
    name:'jack'
}

const man = {
    sayName(){
        return super.name;
    }
}

Object.setPrototypeOf( man, person );

let n = man.sayName();

console.log( n )    //jack

[复制代码](javascript:void(0)

3.super的另类实现

super.name  
等同于   
Object.getPrototypeOf(this).name【属性】  
等同于   
Object.getPrototypeOf(this).name.call(this)【方法】

4.super中的this指向(易混淆)

super.name指向的是原型对象person 中的name,但是绑定的this还是当前的man对象。

[复制代码](javascript:void(0)

const person = {
    age:'20多了',
    name(){
        return this.age;
    }
}

const man = {
    age:'18岁了',
    sayName(){
        return super.name();
    }
}

Object.setPrototypeOf( man, person );

let n = man.sayName();

console.log( n )    //18岁了

[复制代码](javascript:void(0)

Object.getPrototypeOf(this).name指向的是person的name,绑定的this也是person

[复制代码](javascript:void(0)

const person = {
    age:'20多了',
    name(){
        return this.age;
    }
}

const man = {
    age:'18岁了',
    sayName(){
        return Object.getPrototypeOf(this).name();
    }
}

Object.setPrototypeOf( man, person );

let n = man.sayName();

console.log( n )        //20多了

[复制代码](javascript:void(0)

Object.getPrototypeOf(this).name.call(this)指向的是person的name,不过通过call改变了函数的执行上下文,所以this指向的还是man

[复制代码](javascript:void(0)

const person = {
    age:'20多了',
    name(){
        return this.age;
    }
}

const man = {
    age:'18岁了',
    sayName(){
        return Object.getPrototypeOf(this).name.call(this)
    }
}

Object.setPrototypeOf( man, person );

let n = man.sayName();

console.log( n )    //18岁了

[复制代码](javascript:void(0)

防抖与节流函数

防抖

当持续连续触发事件,一定时间内没有再触发时间,事件处理函数才会执行一次.

实际运用: 搜索框、页面大小变化时,echart图表随之变化

<div>
    <input type="text" id="input">
  </div>
  <script>
    //基本方法
    let input = document.getElementById('input')
    let timer = null
    function debounce() {
      clearTimeout(timer)
        timer = setTimeout(() => {
        console.log(input.value);
      }, 1000)
    }
    input.addEventListener('keyup', debounce)
        
        
    //闭包方法
    function debounce() {
      let timer = null
      console.log(123);   //123 只输出一个
      return function() {
        clearTimeout(timer)
        timer = setTimeout(() => {
        console.log(input.value);
      }, 500)
    }
  }
    input.addEventListener('keyup',debounce())
  </script>
   
    

代码分析

input.addEventListener(‘keyup’, debounce()) 的步骤:

  • 先执行debounce函数,此时如果在执行函数则只需要写函数名即可,即:input.addEventListener(‘keyup’, debounce)

  • 创建timer的全局作用域,输入123,消除debounce

  • input.addEventListener(‘keyup’, debounce()), 此后只调用外层创建好的timer,不执行(创建timer的全局作用域,输入123),执行return function。

let ddd = debounce()
input.addEventListener('keyup',function() {
       ddd()
      })   

函数外层有一个function包着,调用时就应该函数名加括号!!

节流

当持续触发事件的时候,保证一段时间内只调用一次事件

设置个闭包来限制timer 作用域。

应用: 节流防抖 图片懒加载

<button id="button">Z点击</button>

document.getElementById('button').onclick = thro(handle, 1000)
    function handle() {
      console.log(456);
    }
    function thro(fn, delay) {
      let timer = true
      return function() {
        if(!timer) {
          return false
        }
        timer = false
        setTimeout(function() {
          fn()
          timer = true
        }, delay)
      }
    }

this

this 永远指向函数运行时所在的对象,而不是函数创建时所在的对象。
匿名函数和不处于任何对象中的函数,this指向window.
call,appply,with 指的this 是谁就是谁。
普通函数调用,函数被谁调用,this就指向谁。

箭头函数和this

箭头函数this指向定义位置时this的指向

匿名函数中的this在严格模式下指向undefined,非严格模式指向window

旧的this是谁调用指向谁,如果是传回调函数的话,会导致函数定义的人不确定this是谁,箭头函数相当于让定义函数的人确定this是谁

因为最后调用 setTimeout 的对象是 window,但是在 window 中并没有 func1 函数。

一、ES5中

其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象

var name = "windowsName";
function a() {
 var name = "Cherry";
 console.log(this.name);   // windowsName
 console.log("inner:" + this); // inner: Window
}
a();   

a是函数,调用a函数的是window! 所以this指向window

var name = "windowsName";
var a = {
 // name: "Cherry",
 fn : function () {
  console.log(this.name);  // undefined
 }
}
window.a.fn();

​ fn是函数,调用fn函数的最后一个对象是a对象! 所以this指向a,但是这里a对象对name没有定义,所以结果为undefined

var name = "windowsName";
var a = {
 name : null,
 // name: "Cherry",
 fn : function () {
  console.log(this.name);  // windowsName
 }
}
var f = a.fn;
f();

这里虽然 f() 复制了 fn() ,但是没有改变调用f()的事实,调用f函数的最后一个对象是window对象!,所以this指向window,返回windowsName

二、箭头函数的this

原理:

  • 箭头函数的this是在定义函数的时候绑定的,而不是在执行函数的时候绑定。
  • 箭头函数中,this指向固定化,并不是因为箭头函数内部有绑定this 的机制,实际上箭头函数根本就没有自己的this,导致内部this就是外层代码块的this,所以不能被当做构造函数。
  • 所谓的定义时候绑定,就是this是继承自父执行上下文中的this,比如这里的箭头函数中的this.x,箭头函数本身与say平级以key:value的形式,也就是箭头函数本身所在的对象为obj,而obj的父执行上下文就是window,因此这里的this.x表示window.x
let x = 11;
let obj = {
    x: 22,
    say: () => {
        console.log(this.x);
    }
}
obj.say()

第一:首先记住:调用 setTimeout 的对象是 window!

第二:箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。意思就是this向外找this!

const a1 = {
      aa() {
        setTimeout(function(){
          console.log(this);         //window
        }),
        setTimeout(() => {			//aa() 返回对象
          console.log(this);
        })
      }
    }
var name = "windowsName";
var a = {
 name : "Cherry",
 func1: function () {
  console.log(this.name)  
 },
 func2: function () {
  setTimeout( () => {
   this.func1()
  },100);
 }
};
a.func2()  // Cherry

this.func1()等于a.func1(),因为this向外找this,找到了a对象

第三:可以用_this = this 来替代

先将调用这个函数的对象保存在变量 _this 中,然后在函数中都使用这个 _this,这样 _this 就不会改变了。

var name = "windowsName";
var a = {
 name : "Cherry",
 func1: function () {
  console.log(this.name)  
 },
 func2: function () {
  var _this = this;            //_this为a对象
  setTimeout( function() {
   _this.func1()
  },100);
 }
};
a.func2()  // Cherry

三、apply、call、bind 函数

fun.apply(thisArg, [argsArray])

thisArg:在 fun 函数运行时指定的 this 值。

argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。

写法:

this.func1().apply(a);

this.func1().call(a)

this.func1().bind(a)( )

var a = {
 name : "Cherry",
 func1: function () {
  console.log(this.name)
 },
 func2: function () {
  setTimeout( function () {
   this.func1()
  }.apply(a),100);
 }
};
a.func2()   // Cherry

参考:https://www.jb51.net/article/124024.htm

前端的this,是会发生变化的。this主要四种常见方式有:

赋值、深浅拷贝

浅拷贝 与 赋值的区别

  • 赋值:当我们把一个对象赋值给一个新的变量时,赋值的是该对象在栈中的地址,该地址指向堆中的数据,所以两个对象联动
  • 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但是引用类型因为共享同一内存,相互影响
  • 深拷贝: 在堆中开辟一个新的区域存放对象,对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响
类型 是否和原对象指向同一对象 修改基本类型是否同一改变 引用类型是否同一改变
赋值
浅拷贝
深拷贝

浅拷贝的实现方式:仅适用于对不包含引用对象的一维数组的深拷贝

  • …mapstate 辅助函数的对象展开符

  • lodash clone

  • concat

  • Object.assign()

深拷贝:

JSON.parse()与 JSON.stringify() 但是JSON.stringify(…) 在对象中遇到 undefined 、 function 和 symbol 时会自动将其忽略, 在数组中则会返回 null (以保证单元位置不变)。

  • $.extend()
  • deepClone

let person = {
      name: '一把实收',
      kids: ['123',[12,45],"gwsd"],
      function() {},
      date: new RegExp('\\w+')
    }
//赋值
    let person1 = person
    console.log("person1",person1);
    
    person1.name = "我是傻逼"
    person1.fuck = "fuck"
    console.log(person1);
    console.log("person",person);
//浅拷贝
	function shallow(obj) {
      let target = {}
      for (let i in obj) {
        if(obj.hasOwnProperty(i))  {
          target[i] = obj[i]
        }
      }
      return target
    }
    let person1 = shallow(person)
    console.log("person1",person1);
    person.name = "牛逼"
    person1.name = "我是傻逼"
    person1.fuck = "fuck"
    person1.kids[0] = 456
    console.log(person1);
    console.log("person",person);
//深拷贝
function shallow(obj) {
      let target = {}
      if(obj === null) return obj
      if(obj instanceof Date) return new Date(obj)
      if(obj instanceof RegExp) return new RegExp(obj)
      if(typeof obj !== "object") return obj
      for (let i in obj) {
        if(obj.hasOwnProperty(i))  {
          target[i] = shallow(obj[i])
        }
      }
      return target
    }
    let person1 = shallow(person)
    console.log("person1",person1);
    person.name = "牛逼"
    person1.name = "我是傻逼"
    person1.fuck = "fuck"
    person1.kids[0] = 456
    person.kids[0] = 456
  
    console.log(person1);
    console.log("person",person);

数组扁平化

arguments对象

在函数体内,标识符arguments是指向实参对象的引用,实参对象是一个类数组对象 arguments[0],arguments.length

arguments是什么?
  答:1:arguments是收到的实参副本
  在词法分析中, 首先按形参形成AO的属性,值为undefined
  当实参传来时, 再修改AO的相应属性.
  2:并把所有收到实参收集起来,放到一个arguments对象里
  t(a,b,c){},
  调用时: t(1,2,3,4,5) 5个参数
  此时 , AO属性只有a,bc,3个属性, arguments里有1,2,3,4,5, 所有的值

对于超出形参个数之外的实参, 可以通过arguments来获得
  3:arguments 的索引 从 0, 1,2,…递增,与实参逐个对应
  4:arguments.length 属性代表实参的个数
  5:arguments一定不是数组, 是长的比较像数组的一个对象,虽然也有length属性
  6:arguments每个函数都会有,因此,arguemnts只会在内部找自身的arguments,
  无法引用到外层的arguments

 // 求圆形面积,矩形面积, 三角形面积
  function area () {
  if(arguments.length == 1) {
  alert(3.14 * arguments[0] * arguments[0]);
  } else if(arguments.length == 2) {
  alert(arguments[0] * arguments[1]);
  } else if(arguments.length == 3) {
  alert(arguments[0] + arguments[1] + arguments[2]);
  } else {
  return null;
  }
  }
  area(10,20,30);


function fn() { 
      console.log(arguments);//Arguments(6) [1, 3, 4, 5, 64, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
      console.log(...arguments);  //1,3,.....
      console.log([...arguments].slice(1)); //[1,3,....]
    }
    fn(1,3,4,5,64,4)

promise

https://zhuanlan.zhihu.com/p/355143116

回调函数分类

1. 同步回调: 
    理解: 立即执行, 完全执行完了才结束, 不会放入回调队列中
    例子: 数组遍历相关的回调函数 / Promise的excutor函数
2. 异步回调: 
    理解: 不会立即执行, 会放入回调队列中将来执行
    例子: 定时器回调 / ajax回调 / Promise的成功|失败的回调

callback

function doSomething(msg, callback){
    alert(msg);
    if(typeof callback == "function") 
    callback();
 } 
doSomething("回调函数", function(){
    alert("匿名函数实现回调!");
 }); 

**含义:回调函数,将一个函数作为参数传递给另一个函数。 **

缺点:

1、没有return

2、形成回调地狱

promise是什么?

1、主要用于异步计算
2、可以将异步对象和回调函数脱离开来,通过.then方法在这个异步操作上绑定回调函数,Promise可以让我们通过链式调用的方法去解决回调嵌套的问题,promise.all这样的方法存在,可以让同时执行多个操作变得简单。

Promise构造函数是同步还是异步执行,then呢?

promise构造函数是同步执行的,then方法是异步执行的

console.log(1)
let a = new Promise((res,rej) => {
   console.log(2);
});
console.log(3);
let b = new Promise((res,rej) => {
    console.log(4);
});
console.log(5);
输出的是12345

console.log(1)
let a = new Promise((res,rej) => {
    console.log(2);
    res();
});
console.log(3);
let b = new Promise((res,rej) => {
    console.log(4);
    res();
});
console.log(5);
a.then(() => {
    console.log(6)
})
b.then(() => {
    console.log(7)
})
答案是1234567

优势

2、并未剥夺函数return的能力,因此无需层层传递callback,进行回调获取数据
代码风格,容易理解,便于维护
3、多个异步等待合并便于解决

步骤

1、resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
2、reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

promise有三个状态:
1、pending[待定]初始状态
2、fulfilled[实现]操作成功
3、rejected[被否决]操作失败
当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
promise状态一经改变,不会再变。

Promise对象的状态改变,只有两种可能:
从pending变为fulfilled
从pending变为rejected。
这两种情况只要发生,状态就凝固了,不会再变了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AhLpD9Ui-1632368985403)(%E5%9B%BE%E7%89%87/JS/v2-0496e6978cf8bbb595cd13c63e6c1a94_720w.jpg)]

错误处理

Promise会自动捕获内部异常,并交给rejected响应函数处理。

错误处理两种做法:

第一种:reject(‘错误信息’).then(() => {}, () => {错误处理逻辑})
第二种:throw new Error(‘错误信息’).catch( () => {错误处理逻辑}) ,推荐使用第二种方式,更加清晰好读,并且可以捕获前面所有的错误(可以捕获N个then回调错误),catch能捕获Error,then也可以捕获Error

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V3ID6soI-1632368985404)(JS.assets/15311104-d93b7cf287478e43.png)]

Promise.all() 批量执行

Promise.all([p1, p2, p3])用于将多个promise实例,包装成一个新的Promise实例,返回的实例就是普通的promise
它接收一个数组作为参数
数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变

当所有的子Promise都完成,该Promise完成,返回值是全部值得数组!!!

有任何一个失败,该Promise失败,返回值是第一个失败的子Promise结果!!

  function cutUp(){
        console.log('开始切菜。');
        var p = new Promise(function(resolve, reject){        //做一些异步操作
            setTimeout(function(){
                reject('切好的菜');
            }, 1000);
        });
        return p;
    }

    //烧水
    function boil(){
        console.log('开始烧水。');
        var p = new Promise(function(resolve, reject){        //做一些异步操作
            setTimeout(function(){
                resolve('烧好的水');
            }, 1000);
        });
        return p;
    }

    Promise.all([cutUp(), boil()])
        .then((result) => {
            console.log('准备工作完毕');
            console.log(result);
        }).catch((result) => {
          console.log('准备工作失败');
            console.log(result);
        })

开始切菜。
开始烧水。
准备工作失败
切好的菜

Promise.race()

顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

封装对象

const obj = {
    name: 'shui'
}
Vue.prototype.obj = obj     //vue.protoytype vue的原型
console.log(obj.name)    //shui
new Promise((resolve, reject) => {
      setTimeout(function() {
        resolve('Hello')
      },1000)
    }) .then(data => {
      console.log(data);
      return data + '123';
    }).then(data => {
      console.log(data);
      return Promise.reject(data + 'error');
    }).then(data =>{
      console.log(data);
    }).catch(data => {
      console.log(data);
      return data + '333';
    }).then(data => {
      console.log(data);
    })

new Promise(resolve => {
    setTimeout(() => {
      resolve('hello')
    }, 2000)
  }).then(val => {
    console.log(val) //  参数val = 'hello'
    return new Promise(resolve => {
      setTimeout(() => {
        resolve('world')
      }, 2000)
    })
  }).then(val => {
    console.log(val) // 参数val = 'world'
  })

若干问题

1、 如何改变promise的状态?
    
    * 1.修改promise状态的几种方式??
*1): resolve(value) : 如果当前状态是pending就会变为resolved
*2): reject(reason) : 如果当前状态是pending就会变为rejected
*3): 抛异常: 如果当前是pending就会变为rejected
    
    
    
2、一个promise指定多个成功/失败回调函数, 都会调用吗?
    
    当promise改变为对应状态时是都会调用的
    const p2 = new Promise((resolve,reject) => {
        //假设异步任务执行成功
        resolve('哈哈!异步任务执行成功啦');
    })
    p2.then(
    	//value:哈哈!异步任务执行成功啦
        value => {console.log('value:' + value);}
    )
    p2.then(
    	//value2:哈哈!异步任务执行成功啦
        value => {console.log('value2:' + value);}
    )

    
3、promise.then()返回的新promise的结果状态由什么决定?
    由then()指定的回调函数执行的结果决定


4、改变promise状态和指定回调函数谁先谁后?
    都有可能 
//常规:先指定回调函数,后改变的状态
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('成功1') //后改变的状态(同时指定数据),异步执行回调函数
        }, 1000)
    }).then(//先指定回调函数,保存当前指定的回调函数
        value => {
            console.log('value1', value)
        },
        reason => {
            console.log('reason1', reason)
        }
    )
 
    //如何向改变状态,再指定回调函数
    new Promise((resolve, reject) => {
        resolve('成功2') //先改变的状态(同时指定数据)
    }).then(//后指定回调函数,异步执行回调函数
        value => {
            console.log('value2', value)
        },
        reason => {
            console.log('reason2', reason)
        }
    )


    
    
5、promise如何串连多个操作任务?
    通过then()的链式调用
    
6、当.then()中有两个回调函数时,只取最前面的
	p.then(
        value => {
            console.log(88888)
          	return value+5;},
        reason => {
            console.log('reason', reason);
            // return Promise.resolve(reason)
        }
    )///88888
    
7、promise异常传(穿)?中断promise链?
中断办法:在回调函数中返回一个pendding状态的promise对象
返回一个这个:new Promise(()=>{}

.catch(reason=>{
        console.log("onRejected1()",reason)

        //注意:没有return的 时候下面的then执行的是成功
        // Promise.reject(reason)

        //下面的then走reason那一条路的 方法
        // throw  reason
        // return Promise.reject(reason)

        //中断:返回一个pedding的promise
        return new Promise(()=>{})
        // return 4
    })

手写promise

1、需要三个准备 完成 拒绝状态,通过状态的变化执行相应的函数

2、需要value值来传递

3、resolve与reject用类方法来写

async/await yield

​ https://zhuanlan.zhihu.com/p/112081953

只是一个语法糖

**async/await **:

两个连在一起用,async说明该function是异步的,await是阻塞功能(函数挂起,等待返回进程),resolve也要执行。

反正就是不管异步同步,等我执行完再来。

注意 : async是微任务,await是同步任务,yield是同步任务

区别:yield两个函数直接有关联,可以拿到return值。yield需要next()去推动执行。

await只是各管各的,执行完就下一个,不管是异步还是同步。

function call(){
    console.log('call funtion');
    return new Promise(resolve=>{
        setTimeout(function(){
            console.log('call funtion timeout');
            resolve('dd');
        },1000);        
    }).then(val => {
      console.log(val);
    });

}
function normalFunction() {
    console.log('normalFunction');
    return 'data'
}
// call(function(){
//     console.log('callback');
// });
async function asynctest() {
    console.log('start');
    await call();
    await normalFunction();
    await new Promise(resolve=>{ console.log('wait result'); resolve()});
    console.log('end');
}
asynctest();

结果:
start
call funtion
call funtion timeout
dd
normalFunction
wait result
end

yield

generator函数:处理异步编程的

function *demo(),yield就是停止,执行完后暂停,如果要继续,则使用.next()函数执行下一个yield。

yield只能在generator函数中。

通过return定义最后一个状态,next()返回值是一个状态:

​ done: 是否遍历成功

​ value: 状态值


    console.log('call funtion');
    return new Promise(resolve=>{
        setTimeout(function(){
            console.log('call funtion timeout');
        },0);  
        resolve('dd');
    });

}
function normalFunction() {
    console.log('normalFunction');
    return 'data'
}
function *yieldFunc() {
    console.log('start');
    yield call();
    yield normalFunction(); 
    console.log('end');
}
let yieldData=yieldFunc();
let firstData=yieldData.next();   //call()
console.log(firstData);      //{value: Promise {: "dd"}, done: false}
firstData.value.then(function(data){
    console.log(data);          //dd
});
yieldData.next();         //normalFunction()
console.log(yieldData);   //yieldFunc {}
start
call function
{value:Promise {<fulfilled>: "dd"}, done: false}
normalFunction
yieldFunc {<suspended>}
dd
call funtion timeout

…展开运算符

https://blog.csdn.net/weixin_45031595/article/details/99695879

ajax和axios

ajax

是什么? 作用

https://zhuanlan.zhihu.com/p/98288927

https://www.cnblogs.com/theblogs/p/10553043.html

如何解决跨域问题? jsonp

https://segmentfault.com/a/1190000007665361

https://baijiahao.baidu.com/s?id=1596094573722602418&wfr=spider&for=pc

var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执⾏函数为onBack
script.src = 'http://www.....:8080/loginuser=admin&callback=onBack';
document.head.appendChild(script);
// 回调执⾏函数
function onBack(res) {
 alert(JSON.stringify(res));

axios

https://www.jianshu.com/p/eb325d70990b

封装:http.js来封装axios,api.js用来管理接口

1、环境切换 :开发环境、测试环境、生产环境

2、设置请求头,设置请求时间

3、请求拦截、响应拦截

4、封装get、post方法

1、在api.js中统一管理接口

2、在页面调用api接口

1.区别
axios是通过promise实现对ajax技术的一种封装,就像jQuery实现ajax封装一样。
简单来说: ajax技术实现了网页的局部数据刷新,axios实现了对ajax的封装。
axios是ajax ajax不止axios。

ES6

不同

1、新增模板字符串 var messag1e = 你好, ${name}, 你今年${age}岁了!

2、箭头函数

箭头函数

箭头函数相当于匿名函数,并且简化了函数定义。

1、箭头函数是匿名函数,不能作为构造函数,不能使用new

2、箭头函数不绑定arguments,取而代之用rest参数…解决

3、箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值

箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。

箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()

4、箭头函数没有原型属性


3、for…of

4、arguments对象可被不定参数和默认参数完美替代

ES6中arguments不管是在严格模式下还是不在严格模式下都不会随着实参改变。

function foo(num1,num2){    //es5这表明在非严格模式下,arguments对象总是会被更新反映出实参的变化。
    //es5这表明在严格模式下,arguments并没有随着实参的变化而变化。
    console.log(num1 === arguments[0]);
    console.log(num2 === arguments[1]);
    num1 = 'a';
    num2 = 'b';
    console.log(num1 === arguments[0]);
    console.log(num2 === arguments[1]);
}
foo(1,2); 
//true
//true
//true
//true

https://blog.csdn.net/hsany330/article/details/100519025

5、将Promise对象纳入规范中,提供原生的promise对象

let var const

1、var:

(1)函数作用域,函数中声明了var,整个函数内都有效;for循环定义var,循环外也可以拿到

(2)在全局作用域内用var声明变量,会在window对象中添加该属性,而let不会

(3)var可以变量提升,let const不行

(4)var可以重复声明,且覆盖前者,let const不行

1、let和const的相同点:

① 只在声明所在的块级作用域内有效。

② 不提升,同时存在暂时性死区,只能在声明的位置后面使用。

③ 不可重复声明。

2、let和const的不同点:

① let声明的变量可以改变,值和类型都可以改变;const声明的常量不可以改变,这意味着,const一旦声明,就必须立即初始化,不能以后再赋值。

const i ; // 报错,一旦声明,就必须立即初始化
const j = 5;
j = 10; // 报错,常量不可以改变

② 数组和对象等复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const只保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个复合类型的变量声明为常量必须非常小心。

7、增加的块级作用域,块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。

8、引入module模块的概念 https://www.cnblogs.com/huancheng/p/9447641.html

forEach、for in 、 for of三者的区别

https://blog.csdn.net/w390058785/article/details/80522383

forEach: 对数组的每一个元素执行一次提供的函数(不能使用return、break等中断循环),不改变原数组,无返回值undefined。

for…in: 循环遍历的值都是数据结构的键值(索引)

for …of : 循环获取一对键值对中的值,一个数据结构只有部署了 Symbol.iterator 属性, 才具有 iterator接口可以使用 for of循环。例子中的obj对象没有Symbol.iterator属性 所以会报错。

哪些数据结构部署了 Symbol.iteratoer属性了呢?

数组 Array
Map
Set
String
arguments对象
Nodelist对象, 就是获取的dom列表集合

https://blog.csdn.net/one_girl/article/details/80192899?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.baidujs&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.baidujs

原型链

https://www.cnblogs.com/loveyaxin/p/11151586.html

原型链实际上就 是构造函数、原型和实例的关系,当对象找不到属性时,就往对象的原型与寻找属性,当找不到时就继续往对象的原型的原型寻找属性,依次向下找。

其实原型对象就是通过 Object 构造函数生成的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ICFHCTiw-1632368985404)(JS.assets/850375-20190708153139577-2105652554.png)]

https://juejin.cn/post/6844903885488783374

Generator函数

https://blog.csdn.net/qq_36667170/article/details/105157081

Generator 函数是 ES6 提供的一种异步编程解决方案。有人称为生成器。

function* greet() {
  console.log(123);
  yield 'ok'
  console.log(456);
  yield 'ook'
  console.log(789);
  return 'oook'
}

let x = greet()    
x.next()                  //输出 123, 遇到yield停止
x.next()                  //输出456



BFC概念

call、apply、bind

区别

1.非严格模式

如果不传参数,或者第一个参数是nullnudefinedthis都指向window

2.严格模式

第一个参数是谁,this就指向谁,包括null和undefined,如果不传参数this就是undefined

call、apply区别:所以 apply 和 call 的区别是 call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。

bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

var a ={
  name : "Cherry",
  fn : function (a,b) {
   console.log( a + b)
  }
 }
 var b = a.fn;
 b.bind(a,1,2)()   // 3

实现

call

Function.prototype.call = function(content) {
    //判断调用call是否是一个函数,this指向调用call的
    if(typeof this!== 'function') {
        throw new TypeError("Not a function")
    }
    //不传参默认为window
    const content = content || window;
    //this指向对象调用的函数
    content.func = this;
    //使伪数组转化为数组
    const args = [...arguments].slice(1)
    //调用函数
    const res = content.func(...args)
    //删除函数
    delete content.func
    return res
}

apply

Function.prototype.apply1 = function(content) {
    if(typeof this!== "function") {
        throw new TypeError("Not a function")
    }
    let res
    const con = content || window
    con.func = this
    if(arguments[1]) {
        res = con.func(...arguments[1])
    }else {
        res = con.func()
    }
    delete con.func
    return res
}

bind

    Function.prototype.myBind = function(context){
      // 判断是否是一个函数
      if(typeof this !== "function") {
        throw new TypeError("Not a Function")
      }
      // 保存调用bind的函数
      const _this = this 
      // 保存参数
      const args = Array.prototype.slice.call(arguments,1)
      // 返回一个函数
      return function F () {
        // 判断是不是new出来的
        if(this instanceof F) {
          // 如果是new出来的
          // 返回一个空对象,且使创建出来的实例的__proto__指向_this的prototype,且完成函数柯里化
          return new _this(...args,...arguments)
        }else{
          // 如果不是new出来的改变this指向,且完成函数柯里化
          return _this.apply(context,args.concat(...arguments))
        }
      } 
    }


 function foo(c, d) {
      this.b = 100
      console.log(this.a)
      console.log(this.b)
      console.log(c)
      console.log(d)
    }
    var func = foo.es3Bind({a: 1}, '1st');
    func('2nd');  // 1 100 1st 2nd
    func.call({a: 2}, '3rd'); // 1 100 1st 3rd
    new func('4th');  //undefined 100 1st 4th

https://blog.csdn.net/u010377383/article/details/80646415

symbol

ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。

定时器滚动图片

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			*{margin:0 ;padding: 0;}
			#banner {
				position: relative;
				width: 37.5rem;
				height: 9.375rem;
				margin: 100px auto;
				overflow: hidden;
			}
			#banner ul {
				position: absolute;
				width: 1200px;
				left: 0;
				top: 0;
			}
			#banner li{
				width: 200px;
				height: 150px;
				float: left;
				list-style: none;
			}
			#banner li img{
				width: 200px;
				height: 150px;
			}
		</style>
	</head>
	<body>
		<div id="banner">
			<ul>
				<li><img src="img/1.png"></li>
				<li><img src="img/2.png"></li>
				<li><img src="img/3.png"></li>
				<li><img src="img/4.png"></li>
				<li><img src="img/5.png"></li>
				<li><img src="img/6.png"></li>
			</ul>
		</div>
		<script type="text/javascript">
			window.onload=function(){
			let oDiv=document.getElementById('banner');
			let oUl=oDiv.getElementsByTagName('ul')[0];
			let oLi=oUl.getElementsByTagName('li');
			let timer=null;
			
			oUl.innerHTML+=oUl.innerHTML;
			oUl.style.width=oLi[0].offsetWidth*oLi.length+'px';
			function moving(){
				if(oUl.offsetLeft<-oUl.offsetWidth/2){
					oUl.style.left='0';
				}
				oUl.style.left=oUl.offsetLeft-2+'px';
			}
			timer=setInterval(moving,10);
			oDiv.onmouseover=function(){
				clearInterval(timer);
			}
			oDiv.onmouseout=function(){
				timer=setInterval(moving,10);
			}
			}
		</script>
	</body>
</html>


//       1.oUl.innerHTML+=oUl.innerHTML;  获得oUl中的内容
		 2.滚动图片是 宽度乘二 左度到半归零
		 3.执行鼠标函数前,需要运行onmouseout的内容先。

this与原型链题目

function Parent() {
      this.a = 1;
      this.b = [1, 2, this.a];
      this.c = { demo: 5 };
      this.show = function () {
        console.log(this.a , this.b , this.c.demo );
      }
    }
    function Child() {
      this.a = 2;
      this.change = function () {
        this.b.push(this.a);
        this.a = this.b.length;
        this.c.demo = this.a++;
      }
    }
    Child.prototype = new Parent();
    var parent = new Parent();
    var child1 = new Child();
    var child2 = new Child();
    child1.a = 11;
    child2.a = 12;
    parent.show(); //1,[1,2,1],5
    child1.show();//11,[1,2,1],5
    child2.show();//12,[1,2,1],5
    child1.change();                 // 原型链是从对象中找属性,找不到才去原型里面找。而构造函数原型只有一个,改变了里面的属性,那些继承它属性的对象在调用原型的属性是也会改变
    child2.change();
    parent.show();
    child1.show();//5,[1,2,1,11,12],5
    child2.show();//6,[1,2,1,11,12],6

JS事件循环机制

https://www.cnblogs.com/hity-tt/p/6733062.html

设计模式

https://zhuanlan.zhihu.com/p/78259452

单例模式

单例模式:

多次实例化,只会执行构造函数一次,提高性能

在一个类只能有一个实例,即使多次实例化该类,也只返回第一次实例化后的实例对象

单例模式能减少不必要的内存开销,并且在减少全局的函数和变量冲突也具有重要的意义。

https://www.jianshu.com/p/7fa6ea107eff

发布订阅模式

定义:当一个对象的状态发生变化时,所有依赖于他的对象都将得到通知

实现:指定一个发布者,给发布者添加一个缓存列表,列表用于存放回调函数以便通知订阅者, 发布者发布消息的时候,会遍历这个缓存列表,触发里面存放的订阅者回调函数

工厂模式

定义:不暴露创建对象的具体逻辑,而是将将逻辑封装在一个函数中,这个函数被视为一个工厂

分类:简单工厂,工厂方法,抽象工厂

区别:简单工厂是将创建对象的步骤放在父类进行,工厂方法是延迟到子类中进行,它们两者都可以总结为:“根据传入的字符串来选择对应的类”,而抽象工厂则是:“用第一个字符串选择父类,再用第二个字符串选择子类”

四大编程思想

面向过程编程

面向对象编程

面向服务架构

面向方面

面向过程(Procedure Oriented 简称PO :如C语言):

从名字可以看出它是注重过程的。当解决一个问题的时候,面向过程会把事情拆分成: 一个个函数和数据(用于方法的参数) 。然后按照一定的顺序,执行完这些方法(每个方法看作一个过程),等方法执行完了,事情就搞定了。

面向对象(Object Oriented简称OO :如C++,JAVA等语言):

看名字它是注重对象的。当解决一个问题的时候,面向对象会把事物抽象成对象的概念,就是说这个问题里面有哪些对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,问题得到解决。

H5C3

https://www.jianshu.com/p/b3762ca713b1

https://www.html.cn/qa/html5/22947.html

1、新增标签,header,footer,nav,aside,article,section

2、本地存储。localstorage、sessionstorage、indexedDB本地存储

3、离线web应用

4、表单新增功能。input通过form属性绑定form表达id,可以写道外面来

5、css5.新增伪类选择器 before、after、first-child、nth-child。新增效果:box-shadow、text-shadow、background-size

animation,transition,transform

6、地理定位,新增方法geolocation , window.navigator.geolocation ,包含三个方法:

getCurrentPosition()
    watchPosition()
    clearWatch

事件流

https://www.cnblogs.com/sunyan-blog/p/11869040.html

事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流。

DOM事件流分为三个阶段,分别为:

捕获阶段:事件从Document节点自上而下向目标节点传播的阶段;

目标阶段:真正的目标节点正在处理事件的阶段;

冒泡阶段:事件从目标节点自上而下向Document节点传播的阶段。

事件委托

http://www.itcast.cn/news/20201020/1024078755.shtml

就是利用事件冒泡原理,子元素的事件会冒泡到父元素,可以只给父元素添加事件,通过事件目标判断元素。

优点:不用生成多个函数,节省内存,动态添加的子元素也包含事件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XT7zSHHA-1632368985405)(%E5%9B%BE%E7%89%87/JS/20201020102654420.jpg)]

继承

https://blog.csdn.net/weixin_43972437/article/details/94340482

构造函数继承 原型链继承 组合继承 es6的extends

//构造函数继承   
  function Person() {
    this.name = 'jack'
    this.say = function() {
      console.log('say');
    }
  }

  Person.prototype.add = function() {   //无法继承原型对象,所以只继承到构造函数内的属性
    console.log('add');
  }

  function Student() {
    Person.call(this)
    this.age = 11
  }

  let out = new Student()
  console.log(out);    //Student {name: "jack", age: 11, say: ƒ}
  console.log(out.add);  //undefined

  //原型链继承
  function Person() {
    this.name = 'jack'
    this.arr = [1,2]
    this.say = function() {
      console.log('say');
    }
  }
  Person.prototype.add = function() {   
    console.log('add');
  }
  function Student() {
    this.age = 11
  }
  Student.prototype =new Person()      
  
  let s1 = new Student()                //实例化原型对象的属性是引用类型的时候,会出现浅拷贝的情况,数据相互影响
  let s2 = new Student()
  s1.arr.push(3)
 
  console.log(s1,s2);


  //组合继承
  function Person() {
    this.name = 'jack'
    this.arr = [1,2]
    this.say = function() {
      console.log('say');
    }
  }
  Person.prototype.add = function() {   
    console.log('add');
  }
  function Student() {
    Person.call(this)                  //2.执行Person.call(this),此时this为s1,与s2区别开来了,但是这里执行了第二次的Person,优化: 所以可以将new Person()改为 Person.prototype
    this.age = 11
  }
  // Student.prototype =new Person()      //1.new Student时执行了Person,获取Person原型属性。  
  Student.prototype = Person.prototype    
  
  let s1 = new Student()                
  let s2 = new Student()
  s1.arr.push(3)
  console.log(s1.prototype);
  console.log(s1,s2);



es6的extends

class Animal {
	constructor (name) {
		this.name = name
	}
	showName () {
		alert(this.name)
	}
}
class Cat extends Animal {
	constructor (name) {
		super(name)
		this.type = '宠物'
	}
}

var cat = new Cat('飞翔的哔哔鸡')
cat.showName()  //飞翔的哔哔鸡

super关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用。当作函数使用时,super代表父类的构造函数,并在子类中执行Parent.apply(this),从而将父类实例对象的属性和方法,添加到子类的this上面。以下三点需要特别注意:

1)子类必须在constructor方法中调用super方法,如果子类没有定义constructor方法,constructor方法以及其内部的super方法会被默认添加。
2)在子类的constructor方法中,只有调用super之后,才可以使用this关键字,否则会报错。
3)super作为对象时,在子类中指向父类的原型对象。即super=Parent.prototype。

class Animal {
	constructor (name) {
		this.name = name
	}
	showName () {
		alert(this.name)
	}
}
class Cat extends Animal {
	constructor (name) {
		super(name)
	}
	sayMy () {
		super.showName()
	}
}
var cat = new Cat('飞翔的哔哔鸡')
cat.sayMy() //飞翔的哔哔鸡

static关键字

在方法前添加stiatic,该方法不会被实例继承,但是父类的静态方法,可以被子类继承

class Animal {
	static showWhat (name) {
		alert(name)
	}
	showName (name) {
		showWhat(name)
	}
}
class Cat extends Animal {}


Cat.showWhat('小猪') //小猪  //父类的静态方法,可以被子类继承
var an = new Animal()
an.showWhat('小狗') //Uncaught TypeError: an.showWhat is not a function

宏任务 微任务 同步队列

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理:

https://segmentfault.com/a/1190000017204460

顺序:

同步任务(主线程) --> 微任务 --> 宏任务

定时器模块

宏任务: settimeout ,

​ dom渲染(所以script(同步)放在dom后面,使dom先渲染。 dom更新时节点变化需要等待事件循环,并不是更新一个节点就渲染一次,是有多个任务在异步队列中的)

微任务:promise,async也是promise

setTimeout(() => {          //宏任务  定时器放在定时器模块,当运行时就开始计时,轮到它时就直接进入宏任务,调用进同步任务
  console.log("定时器1");
  new Promise(resolve => {
    console.log("settime promise");
    resolve();
  }).then(() => {
    console.log("settime timeout");
  })
}, 0)

new Promise(resolve =>{      //微任务
  console.log("promise");
  resolve();
}).then(() =>{
  console.log("promise then");   
})

console.log("同步任务");           

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cSSF9oxs-1632368985406)(%E5%9B%BE%E7%89%87/JS/image-20210904162721572.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XnwKp4g0-1632368985406)(%E5%9B%BE%E7%89%87/JS/image-20210904162612349.png)]

当任务过大时,可以拆分任务,好处是不需要只等它一个,可以同时允许别的。

原理:用settimeout分多次,放入宏队列

let num =4563245   //进行递减累加
let count = 0
function hd() {
  for(let i = 0; i < 100000; i++) {   //分100000次输出
    if(num <= 0) break;
    count += num--; 
  }
  if(num > 0) {         
    setTimeout(hd)              //嵌套 
  }
  console.log(count);
}
hd()   

async和await

async function async1() {
  await async2()
  console.log('async1 end')
}
async function async2() {
  console.log('async2 end')
}
async1()

//await是同步,’async2 end‘是在同步队列, async是异步,在微队列中
console.log('script start')

async function async1() {
  await async2()
  console.log('async1 end')
}
async function async2() {
  console.log('async2 end')
}
async1()

setTimeout(function() {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

console.log('script end')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VH9889WP-1632368985407)(%E5%9B%BE%E7%89%87/JS/image-20210904170605375.png)]

同步:console.log(‘script start’) console.log(‘async2 end’) console.log(‘Promise’)

微队列:console.log(‘async1 end’) console.log(‘promise1’) console.log(‘promise2’)

宏队列:console.log(‘setTimeout’)

构造函数静态,实例成员

https://blog.csdn.net/songlf521/article/details/60144182

js的构造函数(在别的后台语言上叫做类)上可以添加一些成员,可以在构造函数内部的this上添加,可以在构造函数本身上添加,通过这两种方式添加的成员,就分别称为实例成员和静态成员。

1、实例成员和静态成员
实例成员:构造函数中this上添加的成员
静态成员:构造函数本身上添加的成员

function Person(name,age){
    this.name = name ;
    this.age = age;
    this.sayHi = function(){
    console.log('Hello...')
    }
    //上述的name\age\sayHi就是实例成员
}
Person.hobby = 'running'
Person.climb = function(){
    console.log('Climbing...')
}
//上述的hobby和climb就是静态成员
Person.prototype.jump = function(){
    console.log('Jumping...')
}
//jump为p1的__proto__属性指向的它的原型对象上的成员
//===============那么,
var p1 = new Person('Lucy',29)
p1.name  //'Lucy'
p1.age  //29
p1.sayHi() //Hello...
p1.jump()  // Jumping...
Person.climb() //Climbing...
Person.hobby  //running...
//==============但是
p1.climb()  //报错
Person.sayHi()  //报错
Person.jump() //报错

2、总结

实例成员,只能由实例化的对象来访问 p1.jump()
静态成员,只能由构造函数本身来访问 Person.climb()
实例化对象的proto指向构造函数的prototype属性指向的对象,实例化的对象可以访问到它后者身上的成员

栈堆,数组链表

栈和堆

一、程序的内存分配方式不同

  • 栈区(stack):编译器自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构的栈。

  • 堆区(heap):一般是由程序员分配释放,若程序员不释放的话,程序结束时可能由OS回收,值得注意的是他与数据结构的堆是两回事,分配方式倒是类似于数据结构的链表。

二、申请的大小限制不同

  • 栈:在 windows 下,栈是向低地址扩展的数据结构,是一块连续的内存区域,栈顶的地址和栈的最大容量是系统预先规定好的,能从栈获得的空间较小。

  • 堆:堆是向高地址扩展的数据结构,是不连续的内存区域,这是由于系统是由链表在存储空闲内存地址,自然堆就是不连续的内存区域,且链表的遍历也是从低地址向高地址遍历的,堆得大小受限于计算机系统的有效虚拟内存空间,由此空间,堆获得的空间比较灵活,也比较大。

三、堆和栈的存储内容不同

  • 栈:在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令的地址,然后函数的各个参数,在大多数的 C 编译器中,参数是从右往左入栈的,当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令。

  • 堆:一般是在堆的头部用一个字节存放堆的大小,具体内容由程序员安排。


数组链表

数组是最基本的数据结构,所开辟的内存空间是连续的,且内存大小一经确定之后便无法再更改。浪费内存,缺乏弹性(不能根据当前实际需求更改大小)。

优点:查找速度快。通过数组的索引得到对应的数据,另一方面因为存储数据的内存连续,就算不知道所需要的数据对应的索引,即便从头到尾顺序查找一遍也能快速得到想要的数据。

缺点:增添和删除的效率

存储数据的内存不需要连续的,链表中的数据可以存储在内存的任何地方,这是因为链表中的每个数据都存储了下一个链表的地址,从而使离散的内存空间联系在一起,能合理的利用内存。每个链表包含多个节点,每个节点又包含数据域和引用域。

优点为:添加和删除元素十分方便。只需要知道修改前一个元素指向的地址即可。

缺点。查找元素麻烦。如果要查找链表中的一个元素,需要从第一个元素开始,依次往下,直到找到需要的元素位置。

你可能感兴趣的:(js,javascript)