[toc]
Javascript
动、静态语言: 声明的变量是否可以存储不同类型的值,在编译阶段会检测类型
强、弱类型语言: 其产物是,是否允许不同类型值之间进行计算
js(动态弱类型语言)
c|c++ 编译型语言 将程序编译成机器语言
java python 解释型语言(java 将程序编译成字节码:理解为一种中间语言,再由JVM将字节码再翻译成机器语言)
void 运算符 对给定的表达式进行求值,然后返回 undefined
放在运算符中的函数声明 在执行阶段是是找不到的
if(function a() {}) { 找不到 a }
区域注释被展开 采用
// #region // #endregion
encodeURL()不会对本身属性url的特殊字符进行编码,例如“冒号、正斜杠、问号、井号”;而encodeURLComponent()则会对它发现的任何非标准字符进行编码
- 全局变量 防止未定义 请用window.variable调用 如果不加window可导致报错,阻塞后续代码执行
- console 输出占位符
console.log('String: %s, Int: %d,Float: %f, Object: %o', 'string', 1, 1.23, { name: 'name' })
console.log('%c红色%c绿色%c蓝色', 'color:red','color:green', 'color:blue')
// %c后要加空格,padding和line-height实测有效;兼容性不好,谷歌基本都支持
console.log('%c ', 'padding:0 1000px;line-height:1000px;background:url(httpxxxx) no-repeat')
> location
> copy(location)
> ctrl + c // 输出拷贝的 location
.trace() // 输出一个堆栈跟踪
.table(["apples", "oranges", "bananas"]) // 输出表格
// 更多参考: https://developer.mozilla.org/zh-CN/docs/Web/API/Console
- let 是块作用域,一个函数或一个for或if语句中,其定义的值并不会挂载到window上
- const 常量索引 只可以在声明的时候赋值 变量名在内存中的指针不会变,但是指向这个变量的值可以变
实现 let
for(let i = 0; i<10 ;i++) {} console.log(i) // i is not defined
// 实现, 换一个变量名
for(var _i = 0; _i<10; _i++) {} console.log(i) // i is not defined
在表达式中:var a = i++
a等于i原来的值,var a = ++i
a等于i加1后的值;但最后i的值都会加1
fn(++i)
函数传参时 如果要加一 用 ++i, 用 i++ 传递的是i
实现 const (原理:将其设置为window的属性时,重置其读写权限)
function constFn(key, value) {
window[key] = value
Object.defineProperty(window, key, {
enumerable: false,
get() {
return value
},
set() {
throw new TypeError('Assignment to constant variable.')
}
})
}
constFn('name', 'hew')
console.log(name);
name = 'new name'
- Web Worker 提供了在后台非主线程执行 JavaScript 代码的能力
不会阻塞main.js 中代码的执行,否则会一直等待循环执行完毕才会执行下面的代码
// main.js
var worker = new Worker('./index.js')
worker.postMessage = function(e) {
console.log(e);
}
// index.js
while (1) {
var n = Math.random()
if(n>.99999999) {
console.log(n);
postMessage(n)
break
}
}
节点操作
- 注意Element 与 Node 区别
- 当把获取到的节点添加到DOM中的另外一个地方时,原位置的节点没有了,移动到文档片段也是同理;相当于移动节点位置
document.createDocumentFragment()
- 创建新的文档片段
- DocumentFragments 是属于DOM节点的
var fragment = document.createDocumentFragment();
var li = document.createElement('li');
li.textContent = 'li';
fragment.appendChild(li)
MutationObserver 监听DOM是否被修改
var obs = new MutationObserver(function () {
//这里是回调函数
console.log('DOM被修改了!');
/* 微任务队列 异步执行,所有变动触发之后,记录在数组中,统一进行回调 */
});
var article = document.getElementById('i12')
obs.observe(article, { attributes: true, childList: true, subtree: true });
// 打开注释可发现回调被执行
article.innerHTML = '111111'
console.log('11');
article.innerHTML += '99999'
console.log('22');
// 返回 11; 22; DOM被修改了!(并没有输出两次)
zepto.min.js 加载新的模块 直接在 https://github.com/madrobby/zepto/tree/master/src 将需要的模块拷贝到min.js中
try catch finaly
- 如果catch和finally中再抛出异常需要外部再添加try-catch catch的参数可以省略
- 它们任意一个中有return,都会阻塞其后续函数体内容执行,返回值会作为函数返回值
try{
} catch (error) {
// 这里没有作用域 只有参数有个相对的作用域
} finally {
// 无论是否有异常,finally中的语句都会执行,即使try,catch中有return
}
throw 方法:
throw 表达式
抛出的值可以是字符串,数字,布尔值,对象
用此方法抛出错误,后续的代码将不会执行,错误会被catch捕获,如果没有catch将其捕获,程序将终止
// 抛出一个自定义的对象
class ValidateError {
constructor(msg) {
this.name = 'validateError';
this.msg = msg;
}
}
try{
throw new ValidateError('throw error');
}catch(error){
console.log(`${error.msg}-${error.name}`) // throw error-validateError
}
new Error([message[, fileName[, lineNumber]]])
- chrome 不支持后两个参数
- 用new和不用,输出的结果一样
- 还有其它类型错误:ReferenceError(引用错误),TypeError(类型错误),RangeError(范围错误) 等
var err = new Error('909090')
err.code = 56
console.log(JSON.stringify(err)) // { code: 56 }
// axios 的错误也是类似的封装 https://github.com/axios/axios/blob/v1.x/lib/core/AxiosError.js
setTimeout | setInterval
- setInterval 一定要确认清除
- 第三个及之后的参数都作为回调函数的参数
- 同时设置时间0 哪个代码在前 执行哪个
requestAnimationFrame
- 回调函数在重绘前调用
- 浏览器频率是16.7ms(1s/60),setTime设置时间小于这个值时会出现帧丢失的情况
- 而requestAnimationFrame是不用设置时间的,设备的时间绘制间隔是好久,它就是好久
- 当页面没有激活的时候,它会被停止调用,而 setTimeOut 不会
- 回调函数:有一个参数,就是触发该函数的当前时间,可以打印出来
function step(timestamp) {
if (满足条件) {
window.requestAnimationFrame(step);
}
}
window.requestAnimationFrame(step);
- 逻辑运算符
expr1 && expr2 如果 expr1 能被转换为 false,返回 expr1;否则,返回expr2。&&用于布尔值时,当操作数都为 true 时返回 true;否则返回 false.
expr1 || expr2 如果 expr1 能被转换为 true,返回 expr1;否则,返回expr2。|| 用于布尔值时,当任何一个为 true 则返回 true;都是 false 返回 false。
!expr 如果能够转换为 true 则返回 false;否则返回 true。
&& 可以用于获取对象值:const val = a && a.c 如果a为true返回 a.c的值
表达式
是由运算元和运算符(可选)构成,并产生运算结果的语法结构(了解即可,不必纠结).
- 所有的表达式都有返回值(没有返回undefined)
- 函数调用是表达式
- this, null, arguments,变量,字面量(仅包括数字、布尔值、字符串、正则字面量)
- 逗号表达式
some = (1+1, 2+3, 9-8)
给some 赋值 1,因为返回的是最后一个逗号后的计算值,注意这如果不加括号返回2
语句
- 典型的有循环语句和if语句,
- 使某事发生的指令,没有返回值
- 也可以是由大括号括起来的复合语句
- ready 与 onload 的区别
$(document).ready()
是在DOM结构载入完后执行的,而window.onload是在所有文件都加载完后执行的
// 原生实现ready
if(document.addEventListener) {
document.addEventListener('DOMContentLoaded', handler, false);
document.addEventListener('readystatechange', handler, false); //IE9+
window.addEventListener('load', handler, false);
}else if(document.attachEvent) {
document.attachEvent('onreadystatechange', handler);
window.attachEvent('onload', handler);
}
function handler() {
//注销事件, 避免反复触发
document.removeEventListener('DOMContentLoaded',arguments.callee, false);
}
Symble 加了一个description的只读属性 返回描述
正序遍历与反序遍历:反序更快
for(var i=item.length; i--; )
注意最后的分号,该方法会从length-1开始
continue | break | return 区分
break:退出循环或者switch语句,其它地方使用会引起错误!
continue(跳出本次循环):用于
while、do/while、for、for/in
循环语句,其它地方使用会引起错误!return 后面如果需要跟语句必须紧跟,不能换行
return 只能出现在函数体内 语法为:return 表达式
无函数结果,语法为:
return
把控制权返回给页面return false的作用一般是用来取消默认动作的,例如,默认情况下点击一个
<a>
元素
- 文件,图片等数据类型
- Blob : 表示一个不可变、原始数据(二进制)的类文件对象
File 继承自 Blob
// array: ArrayBuffer, ArrayBufferView, Blob, DOMString (一个数组)
// options: { type: 'MIME', endings: 'native'(结束符会被更改为适合宿主操作系统文件系统的换行符) || 'transparent'(默认,保持blob中保存的结束符不变) }
const _Blob = new Blob(array, options );
- 将图片转 base64 方法一:用 ajax 请求图片资源,设置返回值类型为Blob;或者获取input选择的图片
xhr.responseType = 'blob';
let fr = new FileReader();
fr.readAsDataURL(blob);
fr.onloadend = function (e) {
let base64 = e.target.result;
};
方法二:使用canvas
ctx.drawImage(imgElement,176,0,300,150, 0, 150, 300, 150);
const base64 = canvas.toDataURL('image/png');
var ojb=JSON.parse('[{"a":"0","b":"1"}]')
//数字可以不加引号,并且可以是数组与对象的多重结合,也可以只有对象,里面只能用双引号
var ojb=JSON.parse('{"hew":"yes"}',function(key,value){
if(key=='hew'){
return 'hew'+value;
}
else{
return value;
}
});
console.log(ojb) // {hew: "hewyes"}
stringify:
syntax:JSON.stringify(value[, replacer[, space]]) 巴科斯范式(BNF)
- value:数组与对象的结合;
- replacer:函数或是数组;一般设为null;
- space:缩进 | 空格 | 换行 (数字或\t 等);大于 10,则文本缩进 10 个空格;一般设为4
window
方法
window.btoa('需要编码的字符串') // 将字符串转base64
window.atob('base64字符') // 相反方法
// 静态方法会创建一个 DOMString(DOMString 是一个UTF-16字符串,如(`<a id="a"><b id="b">hey!</b></a>`)), 其中包含一个表示参数中给出的对象的URL,这个新的URL对象表示指定的 File 对象或 Blob 对象
// 可以用于预览图片,a标签下载等
window.URL.createObjectURL('File 对象 || Blob 对象 || MediaSource 对象')
const windowObjectReference = window.open(strUrl, strWindowName, [strWindowFeatures]);
// windowObjectReference 打开的新窗口的对象引用,打开新窗口失败返回 null, 用可以用来访问新窗口的属性和方法,只要它符合同源策略的安全要求
// strUrl html 页面也可以是图片文件或者其他任何浏览器支持的文件格式
// strWindowName 新窗口名称,有同名就重载,没有就新打开窗口 也可以是a标签的target属性值
// strWindowFeatures (一般不推荐弹窗方式) 采用弹出窗口 设置窗口样式 left=100,top=100,width=320,height=320 有这些属性可省略 popup
windowObjectReference.print()
windowObjectReference.close()
let tagObejct = null
function openTag(url, tagNmae) {
if (tagObejct === null || tagObejct.closed) {
tagObejct = window.open(url, tagName)
} else {
tagObejct.focus()
}
}
- 利用 a 标签来打开新窗口标签页面 防止被拦截
const openNewWindow = () => {
const a = document.createElement('a')
a.setAttribute('href', 'xxxxx')
a.setAttribute('target', '_blank')
a.setAttribute('rel', '')
// noopener 不授予新的浏览上下文对打开它的文档的访问权限
// noreferrer 阻止浏览器导航到另一个页面时,通过 Referer :HTTP header 将该页面地址或任何其他值作为 Referrer 发送
a.click()
}
- 获取元素计算后的样式
window.getComputedStyle('Element').getPropertyValue('border'); // 返回的是样式设置值,如宽度是百分比,即返回百分比
window.getComputedStyle('Element')['border'];
element.computedStyleMap().get('border') // 实验性质的方法,返回的是真实渲染结果值,比如百分比宽度,返回确切数值
window.localStorage
任何格式存储的时候都会被自动转为字符串
Storage事件(未完)
localStorage.getItem(localStorage.key('第几个保存的值:比如0,1,2等值'))
sessionStorage.getItem(key) // 获取指定key本地存储的值
sessionStorage.setItem(key,value) //将value存储到key字段
sessionStorage.removeItem(key) // 删除指定key本地存储的值
// sessionStorage.length是sessionStorage的项目数
sessionStorage.clear(); //清除所有
// 或者直接设置值
localStorage.a = 3;
localStorage['a'] = 'sfsf';
// https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
location对象:
- location.search // ?及之后的字符串,只有问号时返回空字符串。
- .port // 设置,获取端口号
- .protocol // 同上,协议部分
- .hash // 同上,#号后面的分段
- .host(包含端口)
- .hostname(不包含端口)
- .orging // 返回 协议、主机名和端口号
获取url中的参数:
function getURLParameter(url,name){
var regexp=new RegExp('(^|&)'+name+'=([^&]\*)(&|$)','i');
return url.substring(1).match(regexp)[2];
}
getURLParameter('?name=b&password=d','password')
instanceof
- 要判断的值 instanceof Array|String 返回true或false 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
- [1,2] instanceof Array 判断是否是数组
- data instanceof FormData 判断是否是 FormData
option标签在谷歌和ie低版本上是没有 click 事件的,一般对select都是用change事件;
获取option的value和text用
selectObj=document.getElementById('select');
selectObj.value; selectObj.options[selectObj.selectedIndex].text;
- 可以直接获取到select对象执行以上操作;
- 事件中用this替代selectObj 即可;
- $(selectID).find("option:selected").text()
火狐中是没有innerText,可以用textContent来代替(DOM3的标准)其它的浏览器也可以支持。
null:没有对象
undefined:表示缺少值(声明了变量但没有被赋值;调用函数时,应该提供的参数没有提供,该参数返回undefined;对象没有赋值的属性;函数没有返回值时;)
判断值是否为 undefined 可以 (1)直接判断,当变量没有声明时会出错;(2)使用 typeof 判断返回值是否为 'undefined';
Object
键值 只能是字符或是 Symbol 如果是其它类型会强制转换为这字符
通过
obj[key] = 'xx'
添加、获取属性值时,属性名可以是任何字符串(包括只包含空格的字符串和空字符串)对象转字符串 会默认调用 toString 方法 结果为字符串
[[object Object]]
- 可枚举属性 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
可以用 Object.keys() 获取
- Objetc.keys 返回对象可枚举属性,不包含 原型链和 Symbol
- 对象取值函数
在使用 { ...obj } 时会被执行
var obj = {
name: 'hew',
get getName() {
return 'get ' + this.name
},
set getName(val) {
this.name = val
}
}
console.log(obj.getName); // get hew
obj.getName = 'new name'
console.log(obj.getName); // get new name
- delete
可删除: 1. 可配置对象的属 2. 隐式申明的全局变量(没有用 var let const 声明) 3. const 或 let 申明的 tdz 中的(没有找到验证代码) 4. 修饰属性 configurable 为 false 的
不可删: 1. 从原型继承而来的属性
删数组: length 不会变 删掉的位置变为 empty 用 join 会保留空 1,,3
objName.hasOwnProperty('b')
判断 objName 对象是否有属性 b 返回布尔值 与 in 功能一致
- Object.seal() 密封对象,可以修改属性值,禁止添加新属性
- Object.freeze() 冻结对象,什么都不能修改,只冻结第一层
- Object 获取键值方法
let { keys, values, entries }= Object;// 返回的都是数组
let obj = { a: 1, b: 2, c: 3 };
for(let [key,value] of entries(obj) {
console.log([key, value]);
} // ['a', 1], ['b', 2], ['c', 3];
Reflect
参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
Reflect 是一个内置的对象,用来提供方法去拦截JavaScript的操作。 Reflect不是一个函数对象,所以它是不可构造的,也就是说它不是一个构造器,你不能通过new
操作符去新建或者将其作为一个函数去调用Reflect对象。 Reflect的所有属性和方法都是静态的。
- 防止报错 阻塞程序
call() || apply() || bind()
前两者用法一样,除了传参方式
用法如下:
// 1.子构造函数调用父构造函数的方法
function Fn(name, age) {
this.name = name;
this.age = age;
}
function F1(name, age, num) {
// 让Fn内部的 this 指向F1的实例对象,由于执行了Fn(),所以给实例对象也绑定了Fn中的属性
Fn.call(this, name, age);
this.num = num;
}
var f1 = new F1(1, 2, 3)
console.log(f1.name, f1.age, f1.num); // 1,2,3
// 2.调用函数并指定上下文‘this’
var obj={name:1,age:2};
function f2(){
console.log('other',this.name,this.age);
}
f2.call(obj); // other 1 2
// 3.使用apply和内置函数
Math.min.apply(null,[100,2,3]) // 返回 2
Math.max.apply(null|Math,[1,2,3]) // 返回3
Math.min(2, 3, 1) // 1
Math.max(1, 3, 2) // 3 不传参数返回 infinity (min 也是)
Array.prototype.push.apply(array1, array2);
bind(Object,args,args…)
会创建一个新函数,第一个参数将作为它运行时的this
bind 是返回对应函数,便于之后调用;apply 、call 则是立即调用
var m = { x: 81,getX: function(a,b) { return this.x+a+b; }};
retrieveX=function(a,b){ return this.x+a+b; }
var boundGetX = retrieveX.bind(m,8,1);
boundGetX(); //90
继承
function Fn(name){
this.name=name;
this.type='test'
}
Fn.prototype.do=function(some){
console.log('do:'+some);
}
function F1(name){
Fn.call(this,name);// 不会继承原型方法
}
F1.prototype=Object.create(Fn.prototype)||new Fn(); // 用原型,即可以不用重复声明函数
F1.prototype.way=function(way){
console.log(way)
}
var f1=new F1('koko');
console.log(f1.name);
f1.do('what')
f1.way('what way');
面向对象的实现方式
// 链式方法
function Name() { }
Name.prototype = { f1: function (){console.log('f1'); return this; }, f2: function () {console.log('f2');return this;}}
var n = new Name();
n.f1().f2()
//还可以用构造函数的方式
//工厂模式
function createBlog(name, url) {
var o = {};
o.name = name;
o.url = url;
return o;
}
//构造模式
function Blog(name, url) {
this.name = name;
this.url = url;
}
//原型模式
function Blog() {}
Blog.prototype.name = 'wuyuchang';//只能这样创建
Blog.prototype.url = 'http://tools.jb51.net/';
Blog.prototype.friend = ['fr1', 'fr2', 'fr3', 'fr4'];
navigate.appName/userAgent/appVersion/platform
IE:trident(-ms-);
Firefox:gecko(-moz-);
Chrome:webkit(-webkit-)
Opera:presto(-o-)
window.top.document.compatMode;返回模式(标准和怪异)
location.reload():当参数为false(默认)如果文档没有改变就从缓存中去取,如果改变就在此下载该文档;当参数为true,直接从服务器重新下载该文档。
document.documentElement // :获取 HTML 元素对象 兼容没有问题
.body // :获取body节点对象
.doctype // 获取< !DOCTYPE>
.title='name' // 获取或更改title标签值
.URL // 获取url
.referrer // 获取上一个URL(服务器端)
.forms // 返回文档中的所有Form对象引用
.domain // 获取域名不带端口 (服务器端)
Math 对象
- .abs(X) 返回X的绝对值;
- .ceil(X) 上舍入;返回大于或等于一个给定数字的最小整数
- .floor(X) 下舍入;
- .round(X) 四舍五入;
- .pow(x, y) x的y次方 简写
x**y
- .sqrt() 平方根
- .random() 返回 [0,1) 之间的随机数
- .sin(以弧度表示的角度):(2*PI/360)度数,就是弧度
- .trunc() 方法会将数字的小数部分去掉,只保留整数部分
调用方法返回值为: number
Date 对象
- UTC 世界同一时间 || GMT 格林尼治标准时间 :返回值相同。GMT 是一个时区,而 UTC 是一个时间标准。
- Date()方法:不接受参数,console.log((Date()) // Mon Mar 14 2022 12:51:17 GMT+0800 (中国标准时间)
- 创建Date对象的方法:new Date() 返回当前日期和时间
new Date(毫秒数) 可以带毫秒数的参数,返回日期;这个毫秒数是1970年1月1日到返回日期之间的毫秒时间;
Date('October 13, 1975 11:13:00');
Date('2016-05-13T11:13:00');
Date('2016-05-13 11:13:00’);//在ios系统中可能报错
Date('2016/05/13 11:13:00');
Date(year, month, day, hours, minutes, seconds, milliseconds)
// 参数可以不写全,但是最好是后面的参数不写全;也可以是放Date对象的字符串形式。
- var d=new Date();返回的是对象
d.getTime(); 指定的日期和时间距 1970 年 1 月 1 日午夜(GMT 时间)之间的毫秒数
Date.parse('October 13, 1975 11:13:00’)||(d);
这里可以用d当作参数的原因是d会自执行一下返回一个 datestring
是Date对象的静态方法,不需要用dateObject(就是new出来的d).parse来调用
var d=new Date() d.toISOString() // 2017-01-09T06:19:11.004Z
new Date(+new Date()+836001000).toISOString() 返回正确的时区时间,valueOf() 方法返回一个 Date 对象的原始值,等同于getTime() 。
当给定的时间一样,以下方法返回值相同
var d = new Date('2018-04-03T16:40:00');
d.getTime()
d.valueOf()
Date.now()
Date.parse('date string')
Date.UTC(2018, 3, 3, 8, 40, 0)
循环
for(var key in object ){ // 如果对象是 null 则会自动忽略
console.log(key, object[key])
}
for(var index in array ){ //不推荐
console.log(index, array[index])
}
for...in // 遍历(当前对象及其原型上的)每一个可枚举的非 Symbol 属性
for...of // 不可以遍历原生对象 {a:1,b:2} 因为原生对象不具有 Iterator 接口 (可枚举与不可枚举),可枚举数组,Map Set 和某些部署 Symbol.iterator
- in 的用法
console.log()
// 判断数组是否有该索引
console.log(1 in array) //这里的1表示下标
// 判断某一属性是否在该对象中
console.log('name' in Object) //'name'表示属性名称
switch
- 用命令对象来替换switch
function swichFn(n){
let tempObject={
'1':function(){alert(1)}
}
if(typeof tempObject[n] !== 'function' ) return false ;
return tempObject[n]();
}
swichFn('1');
- 用查表法来替代switch
let result=[0,1,2,3,4]
return result[value]
- 与if-else性能比较;switch较高,但是降低了空间利用率,提高了空间复杂度;但是在数量少的时候用if-else
异步编程
- 函数回调
- 事件监听
- Promises
- generator(ES6)
- async/await(ES7)
- 发布/订阅, 将订阅的回调函数,循环的去执行
async/await
返回的是 Promise; 函数 return 的值,作为 then 的接收参数 async 函数中返回的值,都会被放到 resolve中;await 接收的值是resolve中传递的值
async function foo() { await 1; } // 等价于
function foo() { return Promise.resolve(1).then(() => undefined); }
// 允许函数外部 顶层使用 await
(async function () {
await Promise.resolve());
})();
// 可以直接写
await Promise.resolve());
Function
Thunk 函数 : 只接受回调函数作为参数,将多参函数替换为单参函数
函数重载:js没有,即多个函数,它们的函数名称相同,但是参数个数、类型、顺序不同
function a(b,c=1){}
// 调用a方法 会触发赋值默认值的情况: 1. 没有传参 2. 传递的参数是 undefined
// 定义了默认值的参数,应该是函数的尾参数,非尾部的参数设置默认值,实际上这个参数是没法省略的,除非用undefined代替,以触发该参数等于默认值。
// 设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。初始化结束,作用域消失。不设置参数默认值时,不会出现的。
箭头函数 (param,..) => {}
- 不支持new方法 没有 arguments,super或new.target
- 不会改变指向对象(例如call,apply方法)
- 箭头函数没有它自己的this值,它内部的this继承自外层作用域(箭头函数绑定父级上下文),但是function函数是无论你需不需要都会自动接收一个this,
- 在定义对象方法的时候必须用非箭头函数定义,这些函数会从调用者的作用域获取一个有意义的this
- 内部的this 总是指向函数定义生效时所在的对象,而不是指向运行时所在的作用域。
a={ name:()=>{this}}
这里的this指window,类似匿名函数
参数获取
f(a, b, ...params) {}
与 arguments 差异: 1. params 是数组 不是类数组 2. params 可以值包含部分参数;其它差异未用传递参数
f(...params)
一种特殊的 命名函数表达式
var fnName = function fn() {
// 这里可以调用 fn fnname
}
// 这里只能调用 fnName
- 高阶函数 满足:接收函数作为参数或输出一个函数
- Function() 构造函数
函数实际上是功能完整的对象 。Function类可以表示开发者定义的任何函数。用Function类直接创建函数的语法如下:
var function_name = new Function(arg1, arg2, ..., argN, function_body)
参数必须是字符串,最后一个参数是函数主体(要执行的代码)
同名的变量声明,后者会被忽略;同名的函数声明,前者会被覆盖;同名的函数声明和变量声明,提升函数声明会提升到变量声明之前,变量会被忽略,所以结果是函数声明有效,注意是声明,并没有赋值
- 变量提升 变量提升的过程中,相同的函数会覆盖上一个函数,并且函数优先于变量提升
- 变量提升,就是把变量提升提到函数的top的地方
(function(){ alert(a); // undefined
var a=12;
})()
- 函数提升,把整个函数都提到当前域的top位置,只有声明函数可以提升,表达式函数不能
将函数声明提到函数同级作用域或全局作用域最前面,将函数定义提升到作用域最前面,
注意函数作用域和块级作用域
function a() {
b(); //可以执行
function b(){} // 当运行到定义位置时,会将函数名变量同步到与函数同级作用域,如有同名变量会覆盖其值;可类比为 块级作用域的let不提升,不影响上层作用域
// 如果是非函数的块级作用域,运行到定义位置时就会覆盖全局的同名变量
}
var a = 0
if(true) {
a = 10
console.log(a, window.a) // 10 0
function a() {} // 此处运行后 才会覆盖全局同名变量
console.log(a, window.a) // 10 10
a = 20
console.log(a, window.a) // 20 10 这里的a是函数声明的a 与 window.a 不一样
}
- let 不会变量提升;作用域是块级;不允许同一域重复声明;当在申明前使用,会报错 'Cannot access before initialization'
IIFE: Immediately Invoked Function Expression,意为立即调用的函数表达式 (自执行函数),因为原来只有函数作用域和全局作用域,执行一段代码,又不想污染当前作用域,所以用立即执行函数将其包裹起来,既隔离出了独立的作用域,又能立即执行
(function() {} ());
(function(){})();
(()=>{})() // 只有这一种
!function(){}()
// 感叹号将函数变为了函数表达式,也可用+-||等代
构造:
function A(){
var a1=1;//私有属性
this.a2=2;//公有属性
this.aaa=function(){
alert(a1);//在实例化中是可以访问该私有属性的
}
}
A.prototype.a2 = 3 // 这个在实例化后会被 this.a2 覆盖
A.a3=3; // 静态属性
var obj = new A()
obj.constructor.a3 // 实例中 只能这样访问
this指向
this指的是调用函数的对象。
在函数中用this设置的属性只有在实例化后才能调用。但是在外部用prototype设置的属性可以用prototype来调用。
当构造函数没有返回对象时,会默认返回this,当返回了其它的除变量外,也会默认返回当前构造函数的this。
当参数是匿名函数时,属于全局调用,所以调用对象是window,如setTimeout(function(){},1000)。
(foo.bar = foo.bar)(); (foo.bar, foo.bar)()
这种形式的经过逗号和等号运算符后 就是纯粹的函数了,没有对象引用,内部this指向window
(foo.bar)()
这里的括号只是改变了运算顺序,这里实则没有什么影响,相当于 foo.bar()
String
定义的字符串中需要用到
\
需要将其转义写为\\
字符串存储的大小:理论最大长度是2^53-1
.fromCharCode(97) // a
'a'.charCodeAt(0) // 97
' abcdefg'.trimStart() // 删除字符串开头的空格 同 trimLeft()
'abcdefg '.trimEnd() // 同 trimRight()
.padStart('总长度', '被填充字符串:默认空格') // '789'.padStart(10, '123') // "1231231789"`
.padEnd() // 参数同上,是在字符串尾部添加
.replaceAll() // 'aaaa'.replaceAll('a', 'A') // AAAA
// 当用字符时不是全局替换,函数第一个参数为匹配结果字符,然后依次是子表达式值…,出现位置,被匹配字符串。
.replace('字符|正则', '替换值|函数'); // 返回被替换过后的新字符串
.search(); // 返回匹配字符开始的位置,没有返回-1
.split(); // 以传入的字符或正则匹配的字符分割成数组,删除该匹配的字符,正则如果加了分组就不会删除,必填,没有默认
strA.localeCompare('strB', '语言,默认英语') // 比较两个字符串大小 返回 小于0 第一个字符串小 大于0 第一个字符串大 等于0 相等
// B 码值 66 a 码值 97 但是 a.localeCompare('B') 返回 -1 因为会考虑自然语言的排序 B 是在 a 的后面
数字转字符串 字符串转数字
// 字符串拼接 和 `` 模板字符串 都更快,速度基本相同
// 最慢
toString()
/* 转数字 */
Number(new Date()|new Boolean()|'123');
// 返回的是十进制数
parseInt('该参数会被强制转化为字符串' | '注意科学计数法', '必须,表示第一个参数是什么进制的数')
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/parseInt
parseInt('2+9', 10) // 返回2 因为遇到非指定基数的字符就不解析了
parseInt(0.0000008) === 8 //true
parseFloat('一个参数');
+'10' | ~~'10' // 10 一元运算符 将元素转换为number类型,转换不了就返回NaN。
??
空值合并操作符, 当左边的值为 null 或 undefined 时 返回右边操作数,否则返回左侧操作数
null ?? 1 // 1
10 ?? 1 // 10
a?.b?.c
等价于a&&a.b&&a.b.c
返回 a.b.c 的值a?.b
判断左侧的对象是否为null或undefined
- 字符串之间不能用三元运算符来拼接
var str = '<div class="d-p-people">'+n===1?n:2+'</div>'
- substring(start,end) end 不加表示获取到最后 若end比start小,会先交换这两个参数,若有负数变为0, end 下标取不到
substr(start, length) // 建议不使用,将来会被废弃
charAt:
"Hello world!".charAt(1)
//e当字符数少,且连接数量小于1000时就可以用"+"来连接字符串即可,若是过大,就用数组的join方式来连接,主要是考虑到老浏览器的速度比较慢;
toLocaleLowerCase()和toLowerCase()区别主要是前者会根据地区的语言环境来进行编码。 toUpperCase()
- str.match();
不设置全局,只返回第一个匹配值,并且返回子表达式的值 和 属性index,input。
设置全局,只返回所有的匹配值,不返回子表达式的值。
var str='name=hew;a=c;name=hi';
str.match(/name=([^;]+)(;|$)/g) // ["name=hew;", "name=hi"]
str.match(/name=([^;]+)(;|$)/) // ["name=hew;", "hew", ";", index: 0, input: 'name=hew;a=c;name=hi', groups: undefined]
Number
用Number(参数)来创建数值对象,不推荐用new Number()
Number('true') 返回 NaN
数字 Number 的最大最小范围:Number.MAX_VALUE Number.MIN_VALUE
Number类型统一按浮点数处理,64位存储,整数是按最大54位来算最大最小数的,否则会丧失精度 也就是超过
2^53
(9,007,199,254,740,992) 就会出错;某些操作(如数组索引还有位操作)按32位处理BigInt
BigInt(Math.pow(2, 53)) === BigInt(Math.pow(2, 53)) + BigInt(1)
返回 falseBigInt(1) === 1n
浮点数运算精度问题
0.1+0.2=0.30000000000000004、1-0.9=0.09999999999999998 3*0.2=0.6000000000000001
js 运算是带符号的 一个数除以 0 得无穷 1/0 等于 Infinity -1/0 等于 -Infinity
Number.toString(param) param 可以是2-32 ,以 param 进制显示 ,默认是10,以10进制显示
1.toString()
报错 因为 1. 会优先执行 相当于(1.)toString
(255).toString(2)
可以1.
.1
都是合法的数字
.isFinite()
判断是不是有限值 只有 NaN 正无穷 负无穷 返回 false.isInteger()
判断是不是整数(负整数 0 正整数).isNaN()
判断是否是非数字值,参数值为 NaN(用于判断时,是false) 或字符串、对象、undefined等非数字值则返回 true, 否则返回 false.toFixed(n)
返回字符串 n为0到20之间值; 这里采用的是四舍六入五取偶:
1. 保留位后面是5,保留位是奇数则直接舍去后面数,保留位是偶数则进一位;
2. 保留位后面不是5,按照正常四舍五入计算
(0.35).toFixed(1) // 0.3
(0.45).toFixed(1) // 0.5
es6
字符串
${${}}
//能嵌套
repeat(n) // 字符串重复次数
// n.123 //取整 'r'.repeat(2.23) // 'rr'
// `-1<n<0` //取0
// n=NaN //取0
// n=字符串 //转换为数字
// n=负数(-1等)或Infinity //报错
includes(), startsWith(), endsWith() //都返回boolean值
trim() 两端删除空白字符,去掉换行
标签模板
const str = `a${12}b${34}`
const fn = () => {}
// 函数方式调用
fn`str`
// 相当于fn(['a','b',''], 12, 34)
Promise
一种异步编程解决方案
var promise=new Promise(function(resolve,reject){
// 一些操作,然后判断是 resolve 还是 reject
resolve('这里传递的参数将在then中接收')
})
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo')) // 注意特殊的参数会有不同结果 请参考文档
const p = Promise.reject('出错了'); // 返回一个新的 Promise 实例,该实例的状态为rejected
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
三种状态Pending,Resolve,Rejected
var promise=new Promise(function(resolve,reject){}) resolve和reject由js引擎提供。
promise.then(function(value){//success}), function(value){//failure})
;
then()方法作用是:为Promise实例添加状态改变时的回调函数,分别为resolve和reject指定回掉方法。
Promise.prototype.then()。
then()中返回的值将会被作为参数传递给下一个then(), 如果上一个then返回的是一个Promise对象,这时后一个回调函数(即then),会等待该Promise的状态改变来调用回调函数。catch()
Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止。
一般来说,不要在then中定义使用Reject状态的回调函数,而是用catch。
Promise.any(promises)
返回这一组中第一个成功的那个,如果都没成功就返回一个失败的
Promise.all([Promise实例])
返回一个Promise实例
const pa = Promise.all([p1,p2]) // 所有的 p1 p2 实例的 resolve 执行后, 才会执行 pa 的resolve
pa.then((arr) => { }) // 返回所有实例的 resolve 传入的值构成的数组
串行promise (异步引入高德地图,参见 vue-admin -> utils)
const loadA = new Promise((resolve, reject) => {
console.log('promise 1 start');
resolve(1)
})
const loadB = new Promise((resolve, reject) => {
console.log('promise 2 start');
setTimeout(() => {
resolve(2)
}, 2000);
})
function a() {
return loadA.then((r) => {
console.log('promise 1 end', r);
return loadB
}).then((r) => {
console.log('promise 2 end', r);
return 'finish'
})
}
a().then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
- 一般循环中可以使用
async function start() {
for (let i = 0; i < 3; i++) {
const aa = await a(i)
console.log('wait result in for: ', aa); // 会等待上个请求完毕,才进去下一轮循环
}
console.log('wait result out for: '); // 会等循环体执行完毕再执行
}
function a(pa) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行异步', pa);
resolve(23333)
}, 2000);
})
}
start()
扩展运算符和剩余运算符
[...null, ...undefined] // 抛出异常
{...null, ...undefined} // {}
多个参数(用于函数调用)或者多个元素(用于数组字面量(直接使用的数据值如1,2))或者多个变量(用于解构赋值)。
var args=[1,2,3]
var fn=function(){};
fn(…args);
var arr1 = [0, 1, 2]; var arr2 = [3, 4, 5];
arr1.push(...arr2);
- 数组
[...a, ...b]
不会覆盖重复的值, 不同于对象
const { 0: a, 1: b, 2: c } = [4,5,6]
console.log(a,b,c) // 4,5,6
var [a, [[b], c]] = [1, [[2], 3]];
var [,,third] = ['a', 'b', 'c'];
console.log(third); // c
const [first, ...rest] = ['a', 'b', 'c'];
console.log(rest); // ['b', 'c']
...rest 这种给数组机构赋值的,只能放在最后
- 对象
var { foo: foos, bar: bar } = { foo: 'lorem', bar: 'ipsum' };
console.log(foos) //lorem
// 当变量名和后面属性名一致时可以简化
var { foo, bar } = { foo: "lorem", bar: "ipsum" };
// {...Object1,...Object2}: 后一个对象的属性会覆盖前一个对象的相同属性
// 详见 Object.assign()
const { a: a, b:b=9, c='no' } = { a: 12, b: null }
// 设置默认值简写 { b=9, c='no c' }
console.log(a, b,c); // 12, null on
// 当被赋予的值是 null 时,是不会用默认值将其覆盖的,默认值只会赋值为 undefined 的情况,当没有属性c时才会使用默认值
// Babel是将其解析为判断值是否等于 void 0 也就是 undefined
- 剩余运算符(the rest operator) 需要放最后
var [a, …b]=[1,2,3,4];
console.log(b) //[2,3,4]
const a = { a: 1, b: 2, c: 3, d: 4 }
const { d, ...so } = a
模块功能
<!-- 要启动服务来执行 -->
<script type="module">
import { FN } from './index.js'
console.log(FN());
</script>
i
打印执行当前语句的文件的地址 // 类似 http://127.0.0.1:5500/index.htmlmport.meta export 命名可以出现在任何位置,必须是模块顶层。
引入的同一个模块,如果不做拷贝,当修改其原始值时,会影响所有引用该模块的地方
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。
- es6 模块不是对象 而是通过export输出的代码
- es6 模块是在编译时加载
export var m=1; export function f(){}; // (正确);
var m=1; export m; // (错误)
export { m, f }; // 推荐用法 注意与内部变量建立一一对应关系
var n = 1; export {n as m};
export 与 export default 的区别在于import的时候是不是需要用{},后者不用。
- import '模块名' // 表示引入并执行该模块,多次重复调用只执行一次
import a.js
- import 后的文件位置的.js可以省略
- 由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
整体加载
- 当没有默认导出时要用 * 防止报错 import * as api from './controllers/api'
- import * as obj from 'somefile';(被引入文件是输出多个export,通过该方法集合为一个对象)
- 是可以静态分析的,所以不允许运行时改变
- 允许 obj= 'hello';
export default
- 因为多了个default 所以可以理解为将default与模块内部的变量进行了对应 。
export {add as default}; 等同于export default add;
import { default as xxx } from 'modules'; 等同于import xxx from 'modules';
可同时引入default内容和单个内容 import a, { each } from 'export';
- 复合使用
export { some } from './some';
// 等同于
import { some } from './some'; // 这里 some 输出的 不是 default
export { some };
export { deault as some } from './some';
// 等同于
import some from './some'; // 这里 some 输出的 是 default
export { some };
export * as some from "some";
// 等同于
import * as some from "some"; // 这里 some 输出的 不是 default
export { some };
模块化差异
- es/esm ECMAScript Module
- cjs CommonJs 是 Node 独有的规范
- amd 是由 RequireJS 提出的,推崇依赖前置,提前执行,用户体验好
- CMD 由 SeaJS 提出,推崇依賴就近,延迟执行 ,性能好,用户需要时才执行
CommonJs 模块输出的是一个值的拷贝(当修改模块内部的值,再去获取,还是原来的值),ES6模块输出的是值的引用。 CommonJs 模块是运行时加载,ES6模块是编译时输出接口。
require/exports 是运行时动态加载 动态 import() 运行在 JS 运行阶段;静态 import 运行在编译阶段,总是最先执行; require 同步执行,动态 import()异步执行 返回 promise; require、静态 import、动态 import()都是有缓存的;
import() 是 ESM 动态引入新规范,原理简单的来说就是闭包,输出值的引用
// .then
import('xx.js').then(() => { });
// await
const module = await import('xx.js');
函数扩展
扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符
- 尾递归(只存在一个帧调用,所以效率高,不会出现栈溢出的情况)
尾调用:一个函数执行的最后一步是将另外一个函数调用并返回
每个函数在调用另一个函数的时候,并没有 return 该调用,所以JS引擎会认为你还没有执行完,会保留你的调用帧,所以在return后调用函数,不需要保留外层函数的调用记录
function a(p){ return b(p); }
数组的扩展
- 扩展运算符与函数参数结合
- 当参数是数组时不再依赖apply拆分数组为参数
- 任何 Iterator 接口的对象,都可以通过...扩展运算符转为真正的数组
Proxy | defineProperty | defineProperties
拦截 对目标对象的访问
Proxy(Object,handler)
可监听数组,13中拦截方法 返回对象
如果handler不设拦截,访问proxy就等同与 target
const target = {
name: 'hew'
}
const handler = {
// 三个参数,receiver表示操作行为所针对的对象,一般就是proxy实例
get(target,key,receiver){
if(key in target) {
return target[key]
} else {
throw new ReferenceError(`${key}, does not exist`)
}
},
// 严格模式下,如果没有返回true会报错
// 如果目标对象的属性为writadble,那相对于该属性,set方法失效
// 新增属性也会触发 set
set(target, key, value) {
if (key === 'age') {
if(!Number.isInteger(value)) throw new TypeError('is not integer')
if(value>10) throw new RangeError('need less than 10')
}
target[key] = value
},
// 拦截函数的调用,call,apply操作
apply(target, ctx, args) {
return args
},
deleteProperty(target, key) {
console.log('delete', key);
delete target[key]
return true
}
}
const proxy = new Proxy(target, handler)
console.log(proxy);
// Proxy 是针对Proxy实例(proxy)的,并不针对目标对象(target)
// 所以只有操作 proxy 才会触发拦截 get set 等
Object.defineProperty
Object.defineProperty(obj, 属性名称, descriptor),在现有对象上新建一个属性,或修改现有属性,并返回这个对象
Object.defineProperties(obj, props)
只能对属性进行数据劫持,所以需要深度遍历整个对象
对于数组不能监听到数据的变化
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false
});
const props = {
property: descriptor
}
const descriptor = {
configurable: 默认false;表示属性是否能删除,以及除writeable以外的属性可否被修改;
writable: // 默认 false 属性值可否被修改
enumerable: // 默认 false 属性是否可以被 for...in 和 Object.keys() 获取
value: 属性值
get() {
// 这里注意 又使用 obj.key 形成死循环
}
set() {} 当设置了get或set后不能设置value或writeable
}
Object.create(proto, [propertiesObject])
proto: 新对象的 __proto__
propertiesObject:可选参数,添加到对象上的枚举属性,以及这些属性的描述,名称等,且对应Object.defineProperties()的第二个参数
返回值:指定了原型对象和属性的新对象
const a = Object.create({
name: 'hew'
}, {
age: {
set(value){
console.log("can't set", value);
return value
},
get() {
return 250
}
}
})
a.age=12 // can't set 12
console.log(a) // { age: 250, get age: f, get age: f, _proto_: { name: 'hew' } }
装饰器 Decorator
参考 react-admin/src/pages/practice/decorator.js
装饰类时:添加的静态属性是 name 时,可能报错,不能修改只读属性
装饰类方法:提供了三个参数 target, name, descriptor
es8
Object.entries() // `.entries({ a: 1, b: 2 }) // [['a', 1], ['b', 2]]`
.values()
.getOwnPropertyDescriptors(obj) // 返回对象所有自身属性(非继承属性)的描述对象
.getOwnPropertyDescriptor(obj, key) // 返回某个对象属性的描述对象
.fromEntries() // 将键值对数组转换为对象
.fromEntries([['name', 'hew']])
.fromEntries(new Map([['name', 'hew'], ['scope', '12']])) // {name: 'hew', scope: '12'}
.fromEntries(new URLSearchParams('name=hew&scope=12')) // {name: 'hew', scope: '12'}
.assign(target, source) // 将souse复制到target ,所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
.is(x,y) // 比较两个值是否相等 NaN和NaN为true +0和-0为false