一、改变原始数组的方法:
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。
他们的都是一个查找回调函数。
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
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
使用命名组: 相当于添加属性
/(?
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));
测试不属于NaN数值的值
console.log(isNaN(10)); //false
判断一个值是不是在有限大与有限小之间。
console.log(isFinite(resule));
将其它类型值转换为布尔类型,如空,null,undefined等为false
let b = "hello"
let a = Boolean(b); //ture
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–第四版
obj instanceof Date
意思是: Date的propertypeof是否在obj的原型链上
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"
把字符串转换为大小写
var txt="Runoob";
document.write(txt.toLowerCase() + "
"); //runoob
document.write(txt.toUpperCase()); //RUNOOB
使用符号作为属性
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"}
指数操作
console.log(Math.path(3,2))//9
let s = 3;
s ** = 2;
console.log(s); //9
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)]
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()
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函数没有被正确执行
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中元素个数的数量。 重点在循环次数,不在循环内容
https://blog.csdn.net/kese7952/article/details/79357868
ECMAScript 是javasctipt的一个标准
最新版本已经到6了
大部分浏览器支持es5
script放在head 或者body
一定要双标签
多行备注/****/
严格区分大小写
console.log(num) 打印变量
source,分步操作
application 存放在浏览器
number
Nan //not a number
Infinity //无限大
比较运算符
== 等于 (类型不同。值一样也为ture)
=== 绝对等于(类型一样,值一样 ture)
尽量避免浮点数,精度问题。
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.正常字符串 使用单引号或双引号包裹。
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(1,3)) //sg 包括前不包括后
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"))
'use strict'// 写在script第一行
let i = 1;
预防js的随意性问题
局部变量使用let定义
JavaScript中所有的键都是字符串,值是任意对象
1.动态的删减属性
delete person.age //减
true
person.age = "hh" //增加
"hh"
2.判断一个属性是否在对象中hasOwnProperty()
person.hasOwnProperty('age')
true
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)
}
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
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]
如果两个函数使用同一变量名,互不相干
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)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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.私有成员的存在
我们使用全局变量也可以实现同样的效果
[](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)
super.name
等同于
Object.getPrototypeOf(this).name【属性】
等同于
Object.getPrototypeOf(this).name.call(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指向window.
call,appply,with 指的this 是谁就是谁。
普通函数调用,函数被谁调用,this就指向谁。
箭头函数this指向定义位置时this的指向
匿名函数中的this在严格模式下指向undefined,非严格模式指向window
旧的this是谁调用指向谁,如果是传回调函数的话,会导致函数定义的人不确定this是谁,箭头函数相当于让定义函数的人确定this是谁
因为最后调用 setTimeout 的对象是 window,但是在 window 中并没有 func1 函数。
其实 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
原理:
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
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 (以保证单元位置不变)。
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[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)
https://zhuanlan.zhihu.com/p/355143116
1. 同步回调:
理解: 立即执行, 完全执行完了才结束, 不会放入回调队列中
例子: 数组遍历相关的回调函数 / Promise的excutor函数
2. 异步回调:
理解: 不会立即执行, 会放入回调队列中将来执行
例子: 定时器回调 / ajax回调 / Promise的成功|失败的回调
function doSomething(msg, callback){
alert(msg);
if(typeof callback == "function")
callback();
}
doSomething("回调函数", function(){
alert("匿名函数实现回调!");
});
**含义:回调函数,将一个函数作为参数传递给另一个函数。 **
缺点:
1、没有return
2、形成回调地狱
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);
输出的是1,2,3,4,5
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)
})
答案是1,2,3,4,5,6,7
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([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);
})
开始切菜。
开始烧水。
准备工作失败
切好的菜
顾名思义,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
})
1、需要三个准备 完成 拒绝状态,通过状态的变化执行相应的函数
2、需要value值来传递
3、resolve与reject用类方法来写
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
是什么? 作用
https://zhuanlan.zhihu.com/p/98288927
https://www.cnblogs.com/theblogs/p/10553043.html
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));
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。
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对象
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
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://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
1.非严格模式
如果不传参数,或者第一个参数是
null
或nudefined
,this
都指向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
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
}
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
}
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
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的内容先。
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
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等语言):
看名字它是注重对象的。当解决一个问题的时候,面向对象会把事物抽象成对象的概念,就是说这个问题里面有哪些对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,问题得到解决。
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 编译器中,参数是从右往左入栈的,当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令。
堆:一般是在堆的头部用一个字节存放堆的大小,具体内容由程序员安排。
数组链表
数组是最基本的数据结构,所开辟的内存空间是连续的,且内存大小一经确定之后便无法再更改。浪费内存,缺乏弹性(不能根据当前实际需求更改大小)。
优点:查找速度快。通过数组的索引得到对应的数据,另一方面因为存储数据的内存连续,就算不知道所需要的数据对应的索引,即便从头到尾顺序查找一遍也能快速得到想要的数据。
缺点:增添和删除的效率
存储数据的内存不需要连续的,链表中的数据可以存储在内存的任何地方,这是因为链表中的每个数据都存储了下一个链表的地址,从而使离散的内存空间联系在一起,能合理的利用内存。每个链表包含多个节点,每个节点又包含数据域和引用域。
优点为:添加和删除元素十分方便。只需要知道修改前一个元素指向的地址即可。
缺点。查找元素麻烦。如果要查找链表中的一个元素,需要从第一个元素开始,依次往下,直到找到需要的元素位置。