本篇文章属于知识总结型,概括出许多比较零散的知识点,都是干货噢~css
若是你是小白那么这篇文章正好适合你,若是你是老手那么不妨巩固一下看看还有哪些边角料没补!html
建议:适合有js基础的小伙伴观看,篇幅较长,建议先收藏再慢慢浏览前端
整整花了一周时间总结了一些比较重点也有些比较偏的知识,但愿各位小伙伴慢慢品尝,若是有不对的地方或者是须要优化的地方望请告知,尽可能给你们呈现最有价值的文章。我的水平有限,还请各位大佬指点迷津。但愿各位看了这篇文章能有本身的想法,在前端道路上还很漫长,与我一同探索吧!html5
1、 变量类型node
2、 深拷贝与浅拷贝jquery
3、 原型与原型链ios
4、 实现class与extendsnginx
5、 继承与实现es6
6、 做用域、执行上下文与闭包web
7、 this
9、 同步与异步
12、 改变数组自己的api
十4、 ajax与fetch
十5、 WebSocket
十7、 长链接与短链接
十8、 存储
十9、 跨域
二12、 事件
二十3、 总结
二十4、 其余文章
// 如下结果都为true
console.log([5]==5,['5']==5)
console.log({name:'5'}=='[object Object]')
console.log('5'==5,true==1,false==0)
console.log(undefined==null)
console.log([5,6]=='5,6',['5','6']=='5,6')
复制代码
大白话:优先比较类型,同类型,比大小,非原始,调ToPrimitive,为对象调valueOf,还非原始调toString,最后还非原始则报错,若是为原始则进行类型对比,若是不一样类型再转换,以后对比大小。
所谓==比较就是要转换成同类型比较,若是没法转成同类型就报错
优先比类型,再比null与undefined,再比string和number,再比boolean与any,再比object与string、number、symbol;以上若是转为原始类型比较,则进行类型转换,直到类型相同再比较值的大小。这就是==的隐式转换对比,比较绕,给个图就清晰了!
以下为判断步骤
思考?如何判断此表达式(注意==!与!==) []==![]
虽然过程复杂,记住判断的思路便可,非对象之间,先类型转换再比大小,对象比较则调用获取原始值方法再进一步比较。
以下为toString与valueOf转换
const a=[]
const b=a
a===b //true
---------------
const a=[]
const b=[]
a===b //false
复制代码
判断数组
Array.isArray()
判断数组[] instanceof Array
判断是否在Array的原型链上,便可判断是否为数组[].constructor === Array
经过其构造函数判断是否为数组Object.prototype.toString.call([])
判断值是否为'[object Array]'来判断数组判断对象
Object.prototype.toString.call({})
结果为'[object Object]'则为对象{} instanceof Object
判断是否在Object的原型链上,便可判断是否为对象{}.constructor === Object
经过其构造函数判断是否为对象判断函数
func typeof function
判断func是否为函数func instanceof Function
判断func是否为函数func.constructor === Function
判断是否为函数Object.prototype.toString.call(func)
判断值是否为'[object Function]'来判断func判断null
null===null
来判断是否为null(!a && typeof (a) != 'undefined' && a != 0 && a==a)
判断a是否为nullObject.prototype.__proto__===a
判断a是否为原始对象原型的原型即nulltypeof (a) == 'object' && !a
经过typeof判断null为对象,且对象类型只有null转换为Boolean为false判断是否为NaN
isNaN(any)
直接调用此方法判断是否为非数值一些其余判断
Object.is(a,b)
判断a与b是否彻底相等,与===基本相同,不一样点在于Object.is判断+0不等于-0
,NaN等于自身
prototypeObj.isPrototypeOf(object)
判断object的原型是否为prototypeObj,不一样于instanceof,此方法直接判断原型,而非instanceof 判断的是右边的原型链一个简单的类型验证函数
function isWho(x) {
// null
if (x === null) return 'null'
const primitive = ['number', 'string', 'undefined',
'symbol', 'bigint', 'boolean', 'function'
]
let type = typeof x
//原始类型以及函数
if (primitive.includes(type)) return type
//对象类型
if (Array.isArray(x)) return 'array'
if (Object.prototype.toString.call(x) === '[object Object]') return 'object'
if (x.hasOwnProperty('constructor')) return x.constructor.name
const proto = Object.getPrototypeOf(x)
if (proto) return proto.constructor.name
// 没法判断
return "can't get this type"
}
复制代码
在项目中有许多地方须要数据克隆,特别是引用类型对象,咱们没法使用普通的赋值方式克隆,虽然咱们通常使用第三方库如lodash来实现深拷贝,可是咱们也须要知道一些其中的原理
Object.assign({},obj)
浅拷贝objectobj1={...obj2}
经过spread展开运算符浅拷贝obj2Object.fromEntries(Object.entries(obj))
经过生成迭代器再经过迭代器生成对象Object.create({},Object.getOwnPropertyDescriptors(obj))
浅拷贝objObject.defineProperties({},Object.getOwnPropertyDescriptors(obj))
浅拷贝obj简单实现浅拷贝
// a原拷贝对象,b新对象
for (const key in a) {
b[key] = a[key]
}
------------------------------------------
for (const key of Object.keys(a)) {
b[key] = a[key]
}
复制代码
浅拷贝只拷贝一层属性对于引用类型没法拷贝
JSON.parse(JSON.stringify(obj))
经过JSON的2次转换深拷贝obj,不过没法拷贝undefined与symbol属性,没法拷贝循环引用对象简单深拷贝
//简单版深拷贝,只能拷贝基本原始类型和普通对象与数组,没法拷贝循环引用
function simpleDeepClone(a) {
const b=Array.isArray(a) ? [] : {}
for (const key of Object.keys(a)) {
const type = typeof a[key]
if (type !== 'object' || a[key] === null) {
b[key] = a[key]
} else {
b[key] = simpleDeepClone(a[key])
}
}
return b
}
//精简版深拷贝只能拷贝基本原始类型和普通对象与数组,能够拷贝循环引用
function deepClone(a, weakMap = new WeakMap()) {
if (typeof a !== 'object' || a === null) return a
if (s = weakMap.get(a)) return s
const b = Array.isArray(a) ? [] : {}
weakMap.set(a, b)
for (const key of Object.keys(a)) b[key] = clone(a[key], weakMap)
return b
}
//js原生深拷贝,没法拷贝Symbol、null、循环引用
function JSdeepClone(data) {
if (!data || !(data instanceof Object) || (typeof data == "function")) {
return data || undefined;
}
const constructor = data.constructor;
const result = new constructor();
for (const key in data) {
if (data.hasOwnProperty(key)) {
result[key] = deepClone(data[key]);
}
}
return result;
}
复制代码
比较完善的深拷贝
//深拷贝具体版,非彻底,但大部分均可以
function deepClonePlus(a, weakMap = new WeakMap()) {
const type = typeof a
if (a === null || type !== 'object') return a
if (s = weakMap.get(a)) return s
const allKeys = Reflect.ownKeys(a)
const newObj = Array.isArray(a) ? [] : {}
weakMap.set(a, newObj)
for (const key of allKeys) {
const value = a[key]
const T = typeof value
if (value === null || T !== 'object') {
newObj[key] = value
continue
}
const objT = Object.prototype.toString.call(value)
if (objT === '[object Object]' || objT === '[object Array]') {
newObj[key] = deepClonePlus(value, weakMap)
continue
}
if (objT === '[object Set]' || objT === '[object Map]') {
if (objT === '[object Set]') {
newObj[key] = new Set()
value.forEach(v => newObj[key].add(deepClonePlus(v, weakMap)))
} else {
newObj[key] = new Map()
value.forEach((v, i) => newObj[key].set(i, deepClonePlus(v, weakMap)))
}
continue
}
if (objT === '[object Symbol]') {
newObj[key] = Object(Symbol.prototype.valueOf.call(value))
continue
}
newObj[key] = new a[key].constructor(value)
}
return newObj
}
复制代码
刨析深拷贝(我的思路)
Reflect.OwnKeys(obj)
取出对象自身全部的键,包括Symbol的键也能取出Object.prototype.toString.call(obj)
来进行对象具体类型的判断以上就是本人在实现过程当中的思路,可能讲的比较啰嗦,可是我仍是但愿使用通俗的话让各位明白,表达能力有限,望谅解。
接下来让咱们看看WeakMap的好处
let obj = {
name: {
age: [{
who: 'me'
}]
}
}
let wm = new WeakMap()
deepClonePlus(obj, wm)
obj=null
console.dir(wm) // No properties 即为空
复制代码
从上面能够看出若是原拷贝对象被清空那么WeakMap保存的拷贝表也将被清空,总的来讲方便一点,总比麻烦一点好
看看这种状况
const obj = {
name: {
age: [{
who: 'me'
}]
}
}
let wm = new WeakMap()
console.time('start')
for (let i = 0; i < 1000000; i++) {
deepClonePlus(obj, wm) // wm为手动传入的weakmap
// 此处为了与下面对比,这里故意重置weakmap存储的拷贝值
wm = new WeakMap()
}
console.timeEnd('start') // 耗时2645ms
------------------------------------------------
let wm = new WeakMap()
let m
console.time('start')
for (let i = 0; i < 1000000; i++) {
deepClonePlus(obj, wm)
// 这次为对照组,也执行建立WeakMap可是不重置以前拷贝的wm
m = new WeakMap()
}
console.timeEnd('start') // 耗时73ms
复制代码
从以上对比能够看出若是是屡次拷贝同一对象,最好使用WeakMap来存储拷贝表,那么以后的每次拷贝只需从拷贝表中取出值便可,因为是浅拷贝因此时间较短(注意:不过这种直接从WeakMap中取出的值属于浅拷贝,使用同一个wm对象拷贝出来的都是浅拷贝,若是每一个都须要深拷贝那么只能每次从新建立WeakMap)
为了方便后续讲解,这里先介绍几个知识点:
1、__proto__
属性
对象的
__proto__
属性并不是ECMAScript标准,因为早期没法获取对象原型即对象内部[[Prototype]]属性,各大浏览器厂家对Object.prototype经过访问描述符实现__proto__
的getter与setter来达到访问调用对象的[[Prototype]],[[Prototype]]属性属于对象内部属性没法直接访问,此属性指向对象原型。
__proto__
大体实现
Object.defineProperty(Object.prototype,'__proto__',{
get: function(){
return Object.getPrototypeOf(this) // 获取引用对象的[[Prototype]]
},
set: function(o){
Object.setPrototypeOf(this,o) // 设置引用对象[[Prototype]]属性关联的原型为o
return o
}
})
复制代码
因此本质上是经过访问器属性来获取与设置对象关联的原型,能够理解经过__proto__
能获取与设置原型的引用
这里先把普通对象的__proto__
属性就称呼为对象原型,以便接下来的讲解
2、函数的prototype属性
全部函数都有的prototype属性,js中函数也属于对象的子类型,因此函数也具有对象的
__proto__
与普通对象相似都指向其原型。而这里的prototype属性,是函数独有的。当函数使用new关键字修饰时,咱们能够理解为此函数被看成构造函数使用也就是构造器。当函数被用做构造函数调用时,其prototype发挥了做用,使得由构造器new出来对象的__proto__
指向构造函数的prototype。
如下演示函数prototype属性在实例化时的做用
function Foo(){} // 定义构造函数
console.dir(Foo.prototype) // 定义Foo构造函数时,自动建立的“干净的实例原型”,在原型链第二幅图的左下角有体现
const obj = new Foo() //建立一个实例对象
console.dir(obj.__proto__===Foo.prototype) // true,表名实例关联的原型即为构造函数的prototype指向的原型对象
复制代码
为了便于讲解,这里把函数的prototype称呼为构造器原型,以便接下来的讲解。这里要区分函数的__proto__
属性是做为对象时,关联的原型(即对象原型),函数的prototype
做为构造函数调用时关联的原型(即构造器原型),这里要先弄清楚其中的区别,以便接下来的讲解
3、各种方法与属性的统称
构造函数中定义的方法,咱们统称为静态方法,构造函数中定义的属性咱们统称为静态属性。在原型中定义的属性,咱们统称为原型属性,在原型中定义的方法,咱们统称为原型方法。实例中的属性以及方法,咱们也就称呼为实例属性/方法。固然方法也属于属性,只是咱们一般把定义在对象中的函数称为方法
__proto__
属性,此属性实际上是个访问器属性,并非真实存在的属性,或者能够使用es6的Reflect.getPrototypeOf(obj)
和Object.getPrototypeOf(obj)
方法获取对象的原型,其关系Reflect.getPrototypeOf({}) === Object.getPrototypeOf({}) === {}.__proto__
__proto__
(与普通对象相似),还有一个是函数专有的prototype
属性,由于函数有双重身份,便可以是实例也能够是构造器,因此关系比较特殊Object.prototype
的原型Object.prototype.__proto__
就指向null,字典对象的原型也为null(把对象的__proto__
设置为null,或者使用Object.create(null)
建立一个没有原型的字典对象,可是这个对象仍是属于对象类型),因此原始对象原型(Object.prototype)就是最原始的原型,其余对象类型都要继承自它。这里会详细介绍原型、原型链、实例、构造器的关系 先看最原始的关系
由如上关系能够验证console.log(Function.prototype.__proto__.constructor.__proto__.constructor === Function) //true
__proto__
就是Function,可是构造器的prototype属性指向的原型,是此构造器实例化出来的实例所指向的原型;简单说构造器的prototype就是做为它的实例的原型看看函数的原型链
const Class = (function () {
function Constructor(name) {
this.name = name
}
//添加原型方法
Constructor.prototype.getName = function name(name) {
console.log('原型方法getName:' + this.name);
}
//添加原型属性
Constructor.prototype.age = '原型属性age'
//添加静态方法
Constructor.log = function log() {
console.log('我是构造器的静态方法log');
}
//添加静态属性
Constructor.isWho = '构造器静态属性isWho'
return Constructor
})()
const i = new Class('我是实例')
复制代码
实现class语法糖,只需封装一层函数。
以上只实现class的定义,接下来要实现可以兼容继承的写法
//父类
const Parent = (function () {
function Constructor(age) {
this.age = age
}
Constructor.prototype.getName = function () {
console.log(this.name);
}
return Constructor
})()
//子类
const Class = (function (_Parent = null) {
if (_Parent) {
Constructor.prototype = Object.create(_Parent.prototype, {
constructor: {
value: Constructor,
enumerable: false,
writable: true,
configurable: true
}
})
Constructor.__proto__ = _Parent
}
function Constructor(name, age) {
_Parent ? _Parent.call(this, age) : this
this.name = name
}
Constructor.prototype.getAge = function () {
console.log(this.age);
}
return Constructor
})(Parent)
复制代码
__proto__
function Parent(){} // 定义父类构造器
function Children(){} // 定义子类构造器
let ChildPrototype = Children.prototype // 构造器原型
let ChildPrototypeProto = Children.prototype.__proto__ // 构造器原型的对象原型
// 方法一
ChildPrototypeProto = Parent.prototype // 父类构造器原型做为子类构造器原型(ChildPrototype)的对象原型(ChildPrototypeProto)
//方法二
ChildPrototype = Object.create(Parent.prototype) // Object.create返回一个对象,其__proto__指向传入的参数,也就实现返回的对象继承参数对象
//方法三
Object.setPrototypeOf(ChildPrototype, Parent.prototype) // 直接设置参数1的原型(__proto__)为参数2
复制代码
以上仅实现了原型之间的继承
prototype
与__proto__
,其中prototype
是给实例用的,而__proto__
是给本身用的。__proto__
),来实现静态属性继承function Parent() {} // 定义父构造函数
function Children() {} //定义子构造函数
// 定义父构造函数的静态方法
Parent.foo = function () {
console.log(this.name)
}
// 方法一
Children.__proto__ = Parent // 子构造函数的对象原型指向父构造函数,也就实现继承
// 方法二
Object.setPrototypeOf(Children, Parent) // 同原型继承
console.log(Children.foo) // function(){ console.log(this.name) } ,实现继承
复制代码
以上即为构造函数之间经过对象原型继承静态属性,注:函数也是对象
// 定义父构造函数
function Parent(name) {
this.name = name
}
//定义子构造函数
function Children(name,age) {
Parent.call(this,name) // 这里调用父构造器,实现实例属性继承
this.age = age
}
const obj = new Children('tom', 5)
console.log(obj) // {name: 'tom', age: 5} ,实现实例属性继承
复制代码
经过实例属性继承,能够把父构造器中默认生成的实例属性追加到子构造器实例化出来的对象上
综合以上继承,如今实现真正的继承
// 定义父构造函数,功能:初始化实例name属性
function Parent(name) {
'use strict'
this.name = name
}
// 定义父构造函数的静态方法,功能:设置调用对象的name属性
Parent.setName = function setName(obj, name) {
obj.name = name
}
// 定义父构造器原型(prototype)的方法,功能:获取调用对象的name属性
Parent.prototype.getName = function getName() {
return this.name
}
/*-----以上已定义父类的原型方法(获取name),父类静态方法(设置name),以及构造器默认初始化的属性name------*/
// 定义子构造函数,功能:初始化实例age属性,以及经过父构造器初始化实例name属性
function Children(name, age) {
'use strict'
Parent.call(this, name) // 调用父构造器,初始化name属性
this.age = age // 子构造器初始化age属性
}
// 定义子构造函数的静态方法,功能:设置调用对象的age属性
Children.setAge = function setAge(obj, age) {
obj.age = age
}
// 原型继承
// 设置Children.prototype['[[Prototype]]']= Parent.prototype,此处的'[[Prototype]]'与设置__proto__相同
Children.prototype = Object.create(Parent.prototype)
// 注意此处原型继承以后,不带有constructor属性,应该手动指明为Children
Object.defineProperty(Children.prototype, 'constructor', {
value: Children,
writable: true, // 可写
enumerable: false, // 不可枚举
configurable: true, // 可配置
})
//以上2句能够直接写成一句
/*
Children.prototype = Object.create(Parent.prototype, {
constructor: {
value: Children,
writable: true, // 可写
enumerable: false, // 不可枚举
configurable: true, // 可配置
}
})
*/
// 因为子构造器原型方法必须在继承以后再定义,不然会被继承覆盖
// 定义子构造器原型(prototype)的方法,功能:获取调用对象的age属性
Children.prototype.getAge = function getAge() {
return this.age
}
// 构造函数(继承静态属性)继承
// 设置Children.__proto__ = Parent,注意此处不能使用Children = Object.create(Parent),由于Object.create返回的是一个对象不能替换构造函数
Object.setPrototypeOf(Children, Parent)
// 测试父级
const obj = new Parent('tom') // 实例化父级实例
console.log(obj.getName()) // tom
Parent.setName(obj, 'jerry') // 经过父级静态方法设置name
console.log(obj.getName()) // jerry
console.log(obj instanceof Parent) // true
// 测试子级
const obj1 = new Children(null, 5) // 实例化子级实例
console.log(obj1.getAge()) // 5
Children.setAge(obj1, 8) // 经过子级静态方法设置age
console.log(obj1.getAge()) // 8
console.log(obj1 instanceof Parent) // true
console.log(obj1 instanceof Children) // true
// 完整测试继承
const test = new Children('tom', 5) // 实例化子级实例,name='tom',age=5
console.log(test.getName()) // tom
Parent.setName(test, 'jerry') // 经过父级静态方法设置name=jerry
console.log(test.getName()) // jerry
console.log(test.getAge()) // 5
Children.setAge(test, 8) // 经过子级静态方法设置age=8
console.log(test.getAge()) // 8
class P {
constructor(name) {
this.name = name
}
static setName(obj, name) {
obj.name = name
}
getName() {
return this.name
}
}
class C extends P {
constructor(name, age) {
super(name)
this.age = age
}
static setAge(obj, age) {
obj.age = age
}
getAge() {
return this.age
}
}
// 这里就不带测试了,能够自行验证,比对一下有什么区别
console.dir(Children)
console.dir(C)
复制代码
实现继承,须要对原型、构造器、实例属性都加以实现继承
a=1 // 隐式全局变量 严格模式报错
var b=2 // 显式全局变量
console.log(a,b) //1 2
delete a // 严格模式报错
delete b // 严格模式报错
console.log(b,a) // 2 a is not defined
复制代码
// 如下语句使用let声明不报错,说明为不一样做用域
for (let i = 0; i < 5; i++) {
let i = 5
}
--------------------------------------------
// 此语句报错,说明循环体为条件语句块的子做用域
// for循环执行顺序为:条件语句块1->条件语句块2->循环体->条件语句块3->条件语句块2 依次类推
for (let i = 0; i < 5; i=x) { // x is not defined
let x = 5
}
复制代码
b = 1
function a() {
// 定义b,找到
const b = 2
function s() {
// 使用到b,当前做用域并无,向上找
console.log(b);
}
return s
}
const s = a()
var b = 3
s() // 2
复制代码
这里了解一下函数、变量提高
console.dir(foo) // foo(){}
function foo() {}
var foo = 5
/*
console.dir(foo) // undefined
var foo = 5
*/
------------------------------
var foo = 5
function foo() {}
console.dir(foo) // 5
复制代码
从以上代码结果能够得出结论:
function fn1() {
var name = 'hi';
function fn2() {
console.log(name);
}
return fn2
}
fn1()() // hi
复制代码
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
复制代码
这里看个有趣的东西
function foo(){
let a={name:'me'}
let b={who:'isMe'}
let wm=new WeakMap()
function bar(){
console.log(a) // a被闭包保留
wm.set(b,1) // 弱引用b对象
return wm //wm被闭包保留
}
return bar
}
const wm=foo()()
console.dir(wm) // No properties 即为空
-------------------------------------------
function foo(){
let a={name:'me'}
let wm=new WeakMap()
function bar(){
console.log(a)
wm.set(a,1)
return wm
}
return bar
}
const wm=foo()()
console.dir(wm) // 保存了对象a与其值1
复制代码
先看一张图
function foo(){
console.dir(this) // window ,严格下undefined
}
foo()
-----------------------------------------------
function foo(){
console.dir(this) //非严格Number对象,严格模式 5
}
foo.call(5)
复制代码
严格与非严格模式下的this指向是不一样的,非严格老是指向一个对象,严格模式能够为任意值
执行前
执行后
以上2图能够使用chrome开发工具来进行查看程序执行时的相关数据,能够看到严格模式下简单调用的函数内部的this指向undefined
在没有明确调用者状况下函数内部this指向window,严格模式下都为undefined,除非绑定函数的this指向,才会改变this
// 直接调用函数
function foo() {
console.dir(this) //window,严格下 undefined
function boo(){
console.dir(this) //window,严格下 undefined
}
boo()
}
----------------------------------------------
// 取出对象中的函数,再进行调用
const obj = {
foo: function foo() {
console.dir(this) //window,严格下 undefined
function boo() {
console.dir(this) //window,严格下 undefined
}
return boo
}
}
const foo = obj.foo
foo()()
----------------------------------------------
// 直接经过对象调用函数,再调用返回的函数,能够看出this的指向随调用对象改变
const obj = {
foo: function foo() {
console.dir(this) //obj,严格下 obj
function boo() {
console.dir(this) //window,严格下 undefined
}
return boo
}
}
const foo = obj.foo()
foo()
----------------------------------------------
// 基于回调函数也是如此
function foo(func) {
console.dir(this) // window ,严格下 undefined
func()
}
foo(function () {
console.dir(this) // window ,严格下 undefined
})
复制代码
函数调用也就是在函数名后面加个(),表示调用,若是函数名前没有加任何东西,那么默认为简单调用,在严格与非严格环境下,简单调用的函数内部this指向undefined与window,可是全局环境下的this永远为window
基于对象
当函数做为对象的方法调用时,不受函数定义方式或者位置影响
// 函数this指向调用者对象
const obj = {
foo: function () {
console.dir(this) // obj1,严格下 obj1
function boo() {
console.dir(this) // window,严格下 undefined
}
boo()
return boo
}
}
const obj1 = {}
obj1.boo = obj.foo
obj1.boo()
----------------------------------------------
// 不一样调用对象时,this指向调用者
const obj = {
foo: function () {
console.dir(this) // obj,严格下 obj
function boo() {
console.dir(this)
}
boo() // window,严格下 undefined
return boo
}
}
const obj1 = {}
obj1.boo = obj.foo()
obj1.boo() // obj1,严格下 obj1
----------------------------------------------
// this指向最近的调用者
const obj = {
name: 'obj',
obj1: {
name: 'obj1',
foo: function () {
console.dir(this.name) // obj1
}
}
}
obj.obj1.foo()
复制代码
基于new关键字
// 基于new关键字调用的函数内部this指向实例
function foo() {
console.dir(this) // foo实例
console.log(this instanceof foo) //true
console.log(foo.prototype.isPrototypeOf(this)) //true
that = this
}
var that
const f = new foo()
console.log(that === f) // true
----------------------------------------------
// 嵌套函数内部this与调用函数所在环境的this无关
function foo() {
console.dir(this) // foo实例
function boo() {
console.dir(this) //window,严格下undefined
}
boo()
}
const f = new foo()
复制代码
基于定时器与微任务
微任务中的简单调用的函数this指向window严格下指向undefined,而定时器中的回调函数无论在严格仍是非严格环境下this永远指向window,说明一点,调用window对象的方法时this指向window也就是全局对象,换句话说,简单调用的函数若是属于window自己自带的方法那么这个方法的this指向window
// 异步任务中简单调用的函数都是进入队列,最后由全局环境调用
const id = setInterval(function () {
console.dir(this) // window ,严格下 window
setTimeout(() => {
console.dir(this) // window ,严格下 window
clearInterval(id)
});
})
----------------------------------------------
new Promise(function (resolve, reject) {
console.dir(this) // window ,严格下 undefined
resolve()
}).then(function (res) {
console.dir(this) // window ,严格下 undefined
});
----------------------------------------------
(async function foo() {
function boo() {
console.dir(this) // window ,严格下 undefined
}
await boo()
console.dir(this) // window ,严格下 undefined
})()
----------------------------------------------
// 定时器的回调最终都会被做为简单函数被执行,定时器属于window对象的方法
function foo(){
setTimeout(function (){
console.log(this) //window ,严格下window
})
}
foo.call(5)
----------------------------------------------
// 函数内部的this就是指向调用者,而且能够看出简单调用的回调函数中的this也指向window
const obj = {
foo(callback) {
callback()
console.log(this.foo === obj.foo) // true
console.log(this === obj) // true
}
}
obj.foo(function () {
console.log(this) //window ,严格下undefined
})
----------------------------------------------
// 经过arguments调用的回调函数中的this指向调用者,注意严格与非严格下的arguments对象有所不一样
const obj = {
foo(callback) {
arguments[0]()
console.log(this.foo === obj.foo) // true
console.log(this === obj) // true
}
}
obj.foo(function () {
console.log(this) //arguments对象 ,严格下 arguments对象
})
复制代码
es6引入的箭头函数,是不具备this绑定,不过在其函数体中能够使用this,而这个this指向的是箭头函数当前所处的词法环境中的this对象,能够理解为,this在箭头函数中是透明的,箭头函数包不住this,因此函数内部与外部的this为同一值
// 能够看出箭头函数中的this就是其所在环境的this,箭头函数没法固定this,由其环境决定
const foo = () => {
console.dir(this) //window ,严格下仍是window
}
foo()
----------------------------------------------
// 可见对象中的this指向window,箭头函数中的this指向对象中的this。因为只有建立执行上下文才会绑定this指向,而除了全局上下文,只有函数做用域才会建立上下文环境从而绑定this,建立对象不会绑定this,因此仍是全局this
const obj={
this:this,
foo:()=>{
console.dir(this) //window ,严格下 window
}
}
console.dir(obj.this) //window ,严格下 window
obj.foo()
---------------------------------------------
// 对象方法内部嵌套箭头函数,则此箭头函数的this属于外部非箭头函数this。当调用obj.foo时foo函数建立的执行上下文中的this绑定对象obj,而箭头函数并不会绑定this,因此其this属于foo下的this,即对象obj
const obj = {
foo: function () {
return () => {
console.dir(this) //obj ,严格下 obj
}
}
}
obj.foo()()
复制代码
最简单的方法经过apply、call、bind来给函数绑定this
// 经过变量保留父级this,进行对_this变量修改也就达到修改原this的效果
const obj = {
name: 'obj',
foo: function () {
let _this = this
function boo() {
_this.name = 'OBJ'
console.dir(obj.name) // OBJ
}
return boo
}
}
obj.foo()()
复制代码
这3者的实现其实差很少,bind实现可能会有点不同,都要实现this的改变
Function.prototype.Apply = function (thisArg, args = Symbol.for('args')) {
console.dir(this) //this为这个方法的调用者=>foo函数
const fn = Symbol('fn') //生成一个不重复的键
thisArg[fn] = this || window //把foo函数做为传入this的一个方法
args === Symbol.for('args')
? thisArg[fn]()
: thisArg[fn](...args) //调用这方法,传参
delete thisArg[fn] //使用完删除
}
var name = 'foo'
var age = 5
function foo(age,height) {
console.log(this.name) // obj
console.log(age) // 3
console.log(height) // null
}
const obj = {
name: 'obj',
age: 3
}
foo.Apply(obj,[obj.age,null])
复制代码
基本思路同apply,就是传参形式改变一下,这里经过arguments获取参数列表
Function.prototype.Call = function (thisArg) {
console.dir(this) //this为这个方法的调用者=>foo函数
const fn = Symbol('fn') //生成一个不重复的键
thisArg[fn] = this || window //把foo函数做为传入this的一个方法
const args = Array.from(arguments).slice(1)
args.length ? thisArg[fn](...args) : thisArg[fn]() //调用这方法,传参
delete thisArg[fn] //使用完删除
}
复制代码
bind函数要可以返回严格绑定this与参数后的函数,调用这个返回的函数时有可能还会传入参数,那么须要拼接参数
Function.prototype.Bind = function (thisArg) {
const fn = Symbol('fn') //生成一个不重复的键
thisArg[fn] = this || window //把foo函数做为传入this的一个方法
const f = thisArg[fn] // 负责一份函数
delete thisArg[fn] //删除原来对象上的函数,可是保留了this指向
const args = Array.from(arguments).slice(1)
return function () {
const arg = args.concat(...arguments)
f(...arg)
}
}
var name = 'foo'
var age = 5
var height = 4
function foo(age, height) {
console.log(this.name) // obj
console.log(age) // 3
console.log(height) // 2
}
const obj = {
name: 'obj',
age: 3
}
foo.Bind(obj, obj.age)(2)
复制代码
先看一张图,有个大致架构
如下展现的是事件循环大体流程
如下为主线程判断逻辑
// test.html(主线程)
const w= new Worker('postMessage.js')
w.onmessage=function(e){
console.log(e.data);
}
w.postMessage('b') // b is cat
w.terminate() // 手动关闭子线程
----------------------------------------------
// postMessage.js(worker线程)
this.addEventListener('message', (e) => {
switch (e.data) {
case 'a': this.postMessage(e.data+' is tom')
break;
case 'b': this.postMessage(e.data + ' is cat')
break;
default: this.postMessage(e.data + " i don't know")
this.close() // 自身关闭
break;
}
})
复制代码
模块化的引入主要是用于解决命名冲突、代码复用、代码可读性、依赖管理等
define(id?,dependencies?,factory)
定义模块,id可选,为定义模块的标识,默认为模块文件名不包括后缀,dependencies可选,是当前模块依赖的模块路径数组,factory为工厂方法,初始化模块的函数或者对象,若是为函数将会只执行一次,若是是对象将做为模块的输出require(dependencies,factory)
导入模块,其中dependencies为须要导入的模块路径数组,factory为当模块导入以后的回调函数,此函数的参数列表为对应导入的模块require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min", //实际路径为js/lib/jquery.min.js
"underscore": "underscore.min",
}
})
复制代码
define(id?,deps?,factory)
,id同AMD,deps通常不在其中写依赖,而是在factory中在须要使用的时候引入模块,factory函数接收3各参数,参数一require方法,用来内部引入模块的时候调用,参数二exports是一个对象,用来向外部提供模块接口,参数三module也是一个对象上面存储了与当前模块相关联的一些属性和方法seajs.use(deps,func)
加载模块,deps为引入到模块路径数组,func为加载完成后的回调函数AMD、CMD的主要区别在于
AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块 CMD推崇就近依赖,只有在用到某个模块的时候再去require
module.exports
(不推荐exports)导出模块对象,经过require(模块路径)加载模块__dirname
表明当前模块文件所在的文件夹路径,__filename
表明当前模块文件夹路径+文件名pop()
尾部弹出一个元素push()
尾部插入一个元素shift()
头部弹出一个元素unshift()
头部插入一个元素sort([func])
对数组进行排序,func有2各参数,其返回值小于0,那么参数1被排列到参数2以前,反之参数2排在参数1以前reverse()
原位反转数组中的元素splice(pos,deleteCount,...item)
返回修改后的数组,从pos开始删除deleteCount个元素,并在当前位置插入itemscopyWithin(pos[, start[, end]])
复制从start到end(不包括end)的元素,到pos开始的索引,返回改变后的数组,浅拷贝arr.fill(value[, start[, end]])
从start到end默认到数组最后一个位置,不包括end,填充val,返回填充后的数组其余数组api不改变原数组
window.location===document.location
,其中的属性都是可读写的,可是只有修改href和hash才有意义,href会从新定位到一个URL,hash会跳到当前页面中的anchor名字的标记(若是有),并且页面不会被从新加载// 这行代码将会使当前页面重定向到http://www.baidu.com
window.location.href = 'http://www.baidu.com'
----------------------------------------------
// 若是使用hash而且配合input输入框,那么当页面刷新以后,鼠标将会自动聚焦到对应id的input输入框,
<input type="text" id="target">
<script>
window.location.hash = '#target'
</script>
复制代码
先看下其拥有的属性
这里补充一个origin属性,返回URL协议+服务器名称+端口号 (location.origin == location.protocol + '//' + location.host)
window.addEventListener("hashchange", funcRef);
// 或者
window.onhashchange = funcRef;
复制代码
location方法
assign(url)
,经过调用window.location.assign
方法来打开指定url的新页面window.location.assign('http://www.baidu.com')
在当前页面打开百度,可回退replace(url)
,在当前页面打开指定url,不可回退reload([Boolean])
,调用此方法将会从新加载当前页面,若是参数为false或者不填,则会以最优的方式从新加载页面,可能从缓存中取资源,若是参数为true则会从服务器从新请求加载资源window.navigator
对象包含有关浏览器的信息,能够用它来查询一些关于运行当前脚本的应用程序的相关信息document.write("浏览器的代码名:" + navigator.appCodeName + "<br>");
document.write("浏览器的名称:" + navigator.appName + "<br>");
document.write("当前浏览器的语言:" + navigator.browserLanguage + "<br>");
document.write("浏览器的平台和版本信息:" + navigator.appVersion + "<br>");
document.write("浏览器中是否启用 cookie :" + navigator.cookieEnabled + "<br>");
document.write("运行浏览器的操做系统平台 :" + navigator.platform + "<br>");
复制代码
navigator.appCodeName
只读,任何浏览器中,老是返回 'Gecko'。该属性仅仅是为了保持兼容性。navigator.appName
只读,返回浏览器的官方名称。不要期望该属性返回正确的值。navigator.appVersion
只读,返回一个字符串,表示浏览器的版本。不要期望该属性返回正确的值。navigator.platform
只读,返回一个字符串,表示浏览器的所在系统平台。navigator.product
只读,返回当前浏览器的产品名称(如,"Gecko")。navigator.userAgent
只读,返回当前浏览器的用户代理字符串(user agent string)以下在不一样浏览器打印的信息
/*
chrome:
Mozilla/5.0
(Macintosh; Intel Mac OS X 10_12_6)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/61.0.3163.91 Safari/537.36
safari:
Mozilla/5.0
(Macintosh; Intel Mac OS X 10_12_6)
AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0
Safari/604.1.38
ios11刘海X:
Mozilla/5.0
(iPhone; CPU iPhone OS 11_0 like Mac OS X)
AppleWebKit/604.1.38 (KHTML, like Gecko)
Version/11.0 Mobile/15A372 Safari/604.1
ipad:
Mozilla/5.0
(iPad; CPU OS 9_1 like Mac OS X)
AppleWebKit/601.1.46 (KHTML, like Gecko)
Version/9.0 Mobile/13B143 Safari/601.1
galxy sansum:
Mozilla/5.0
(Linux; Android 5.0; SM-G900P Build/LRX21T)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/61.0.3163.91 Mobile Safari/537.36
安装uc浏览器:
Mozilla/5.0
(Linux; U; Android 6.0.1; zh-CN; Mi Note 2 Build/MXB48T)
AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0
Chrome/40.0.2214.89 UCBrowser/11.4.9.941 Mobile Safari/537.36
winphone:
Mozilla/5.0
(Linux; Android 5.1.1; Nexus 6 Build/LYZ28E)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/61.0.3163.91 Mobile Safari/537.36
hybrid方法的可能:
Mozilla/5.0
(iPhone; CPU iPhone OS 11_0 like Mac OS X)
AppleWebKit/604.1.38 (KHTML, like Gecko)
Mobile/15A372 weibo/80011134
*/
复制代码
window.XMLHttpRequest
构造器实例化一个网络请求对象const XHR = new XMLHttpRequest()
XHR.open(method, url, [ async, [ user, [ password]]])
此方法用来发送一个请求,method为请求方法,url为请求地址,async为boolean值默认为true即便用异步请求,user和password在请求须要用户和密码的时候使用XHR.send(body)
参数为发生请求主体内容,其格式能够为FormData、ArrayBuffer、Document、序列化字符串,在收到响应后,响应的数据会自动填充XHR对象的属性XHR.setRequestHeader(header,value)
设置请求头的类型与值,当以post方式发起请求就用设置XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
此请求头,值可更改XHR.onreadystatechange = function () { }
XHR.responseText
属性为这次响应的数据,为字符串,多是JSON格式须要JSON.parse解析XHR.responseXML
属性为xml形式的数据,能够经过XHR.responseType = 'document'
和XHR.overrideMimeType('text/xml')
来解析为XMLXHR.withCredentials
属性设置为boolean值,经过此属性来设置是否使用cookies、authorization等凭证字段XHR.timeout
经过此属性来设置请求超时时间XHR.ontimeout
经过此属性来设置请求超时的回调函数,函数的参数为事件对象XHR.abort()
此方法用来终止网络请求XHR.getAllResponseHeaders()
此方法用来获取全部的响应头XHR.getResponseHeader(name)
此方法用来获取指定的响应头XHR.addEventListener(eventname,callback)
方法添加对应的事件监听,其回调函数接收一个事件对象参数简单的发起一次请求
// 最简单的发起一个请求
const XHR = new XMLHttpRequest()
XHR.open('get','http://127.0.0.1:3000/test?key=value')
XHR.send()
XHR.addEventListener('load',(e)=>{
// 服务端返回的是查询参数
console.log(XHR.response) // {"key":"value"}
})
复制代码
基于XMLHttpRequest封装一个请求方法
// 发送的数据
const data = {
name: 'tom'
}
// 请求配置
const config = {
type: "post",
url: "http://127.0.0.1:3000/test",
data: data,
dataType: 'application/json',
success: function (res) {
console.log(res);
},
error: function (e) {
console.log(e);
}
}
// 请求构造器
function Ajax(conf) {
this.type = conf.type || 'get'
this.url = conf.url || ''
this.data = conf.data || {}
this.dataType = conf.dataType || ''
this.success = conf.success || null
this.error = conf.error || null
}
// send方法
Ajax.prototype.send = function () {
if (this.url === '') return
const XHR = new XMLHttpRequest()
XHR.addEventListener('load', () => {
if (XHR.status >= 200 && XHR.status < 300 || XHR.status == 304) {
typeof this.success === 'function' && this.success(XHR.response)
}
})
XHR.addEventListener('error', (e) => {
typeof this.error === 'function' && this.error(e)
})
if (this.type.toLowerCase() === 'get') {
XHR.open('get', this.url)
XHR.send(null)
} else {
XHR.open(this.type, this.url)
XHR.setRequestHeader('Content-Type', this.dataType || 'application/x-www-form-urlencoded')
let data = this.data
if (this.dataType === 'application/json') {
data = JSON.stringify(this.data)
}
XHR.send(data)
}
}
// 发送请求
const ajax = new Ajax(config).send()
复制代码
因为网络请求模块封装较繁琐,这里就简单封装了一下,仅供参考(。^▽^)
// fetch方法返回一个Promise对象,可用then方法接收结果,用catch方法捕获异常,同Promise使用
// 配置对象具体配置
const config = {
method: 'GET', // 请求方法
headers: { // 头信息
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
},
body: JSON.stringify({ // 请求的 body 信息,Blob, FormData 等
data: 1
}),
mode: 'cors', // 请求的模式,cors、 no-cors 或 same-origin
credentials: 'include', // omit、same-origin 或 include。为了在当前域名内自动发送 cookie, 必须提供这个选项
cache: 'no-cache', // default 、 no-store 、 reload 、 no-cache 、 force-cache 或者 only-if-cached
redirect: 'follow', // 可用的 redirect 模式: follow (自动重定向), error (若是产生重定向将自动终止而且抛出一个错误), 或者 manual (手动处理重定向).
referrer: 'no-referrer', // no-referrer、client或一个 URL。默认是 client。
referrerPolicy: 'no-referrer', // 指定 referer HTTP头
integrity: 'sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=', // 包括请求的 subresource integrity 值
}
// 发起请求
fetch('http://biadu.com' [, config])
复制代码
then的回调函数接受一个Response对象参数,其对象拥有9个属性,8个方法
9个属性
8个方法
WebSocket(url[, protocols])
建立实例,参数1 对方绝对url其url以ws://
或者wss://(加密)
开头,参数2 protocols是单协议或者包含协议的字符串数组// 必须传入绝对URL,能够是任何网站
const s = new WebSocket('ws://www.baidu.com')
s.readyState // 0 创建链接 1 已经创建 2 正在关闭 3 链接已关闭或者没有连接成功
s.send('hello') // 发送的数据必须是纯文本
s.onopen = function () {}
s.onerror = function () {}
s.onmessage = function (event) {
// 当接收到消息时
console.log(event.data) // 数据是纯字符
}
s.close() // 关闭链接
s.onclose = function (event) {
/*
* event.wasClean 是否明确的关闭
* event.code 服务器返回的数值状态码
* event.reason 字符串,服务器返回的消息
*/
}
复制代码
简单演示
const xhr = new XMLHttpRequest()
// 每秒发送一次短轮询
const id = setInterval(() => {
xhr.open('GET', 'http://127.0.0.1:3000/test?key=value')
xhr.addEventListener('load', (e) => {
if (xhr.status == 200) {
// 处理数据
console.log(xhr.response)
// 若是不须要能够关闭
clearInterval(id)
}
})
xhr.send()
}, 1000)
复制代码
简单演示
function ajax() {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:3000/test?key=value');
xhr.addEventListener('load', (e) => {
if (xhr.status == 200) {
// 处理数据
console.log(xhr.response)
// 若是不须要能够关闭
if (xhr.response != '') return
ajax()
}
})
xhr.send();
}
复制代码
Connection:keep-alive
Connection:keep-alive
来决定使用,而是否轮询,是根据服务端的处理方式来决定的,与客户端没有关系https://localhost:80/
和http://localhost:8080/
的Cookie是共享的,能够经过domain设置域,path设置域下的共享路径.baidu.com
,则a.baidu.com
可访问其上级域名的cookie基本使用
localStorage.setItem("b", "isaac"); //设置b为"isaac"
localStorage.getItem("b"); //获取b的值,为"isaac"
localStorage.key(0); //获取第0个数据项的键名,此处即为“b”
localStorage.removeItem("b"); //清除c的值
localStorage.clear(); //清除当前域名下的全部localStorage数据
复制代码
window.addEventListener("storage", function(e){}
设置localStorage事件监听,当存储区域的内容发生改变时,将会调用回调sessionStorage.setItem(name, num); //存储数据
sessionStorage.setItem('value2', 119);
sessionStorage.valueOf(); //获取所有数据
sessionStorage.getItem(name); //获取指定键名数据
sessionStorage.sessionData; //sessionStorage是js对象,也能够使用key的方式来获取值
sessionStorage.removeItem(name); //删除指定键名数据
sessionStorage.clear();
复制代码
来实现一下吧
// 前端准备
// 定义回调函数
function fn(arg) {
// arg为服务端传来的数据
console.log(`客户端获取的数据:${arg}`)
}
// 建立script标签
const s = document.createElement('script')
// 给script标签的src属性赋值,值为请求url,查询参数callback,需与后端对应
// fn为前端回调函数名
s.src = `http://127.0.0.1:3000/test?callback=fn`
// 向html添加此标签,添加完成以后浏览器自动请求script的src对应的网址
document.getElementsByTagName('head')[0].appendChild(s);
// 等待浏览器收到响应以后,将会自动执行响应内容的代码
----------------------------------------------
// 后端准备
// nestjs(ts)处理
@Controller('test') //api
export class TestController {
@Get() //get方式请求
//取url中的查询参数,即?以后的键值对,键与值对应query对象参数的键与值
callback(@Query() query) {
// 返回的数据
const data = '我是服务端返回的数据';
// 取查询参数,这里的callback要与前端?以后的键名一致,fn即fn函数名
const fn = query.callback;
// 返回结果,格式:函数名(服务器的数据),注意这里须要序列化成字符串,若是参数自己是字符串那么要加引号,前端并不知道data是字符串
return `${fn}('${data}')`;
}
}
// express(js)处理,同上
router.get('/test', async (req, res) => {
const data = '我是服务器返回的数据'
// req.query为查询参数列表
const fn = req.query.callback
// 返回数据
res.send(`${fn}('${data}')`)
})
复制代码
响应内容
Access-Control-Allow-Origin:*
便可开启cors容许跨域请求,使用通配符*表示容许全部不一样域的源访问资源,也可单独设置指定容许的源域名postMessage(data,origin[, source])
data为发送的数据只能发送字符串信息,origin发送目标源,指定哪些窗口能接收到消息事件,若是origin设置为*则表示无限制,source为发送消息窗口的window对象引用,<!-- test.html -->
<iframe src="http://127.0.0.1:5501/postMessage.html"
name="postIframe" onload="messageLoad()"></iframe>
<script>
// 定义加载以后执行的函数,给postMessage.html发送数据
function messageLoad() {
const url = 'http://127.0.0.1:5501/postMessage.html'
window.postIframe.postMessage('给postMessage的数据', url)
}
// 用于监听postMessage.html的回馈,执行回调
window.addEventListener('message', (event) => {
console.log(event.data);
})
</script>
----------------------------------------------
<!-- postMessage.html -->
<script>
// 监听test.html发来的数据,延迟1秒返回数据
window.addEventListener('message', (event) => {
setTimeout(() => {
event.source.postMessage('给test的数据', event.origin)
},1000)
})
</script>
复制代码
window.name
属于全局属性,在html中的iframe加载新页面(能够是跨域),经过iframe设置的src指向的源中更改name的值,同时主页面中的name也随之更改,可是须要给iframe中的window设置为about:blank
或者同源页面便可<!-- test.html -->
// 封装应该用于获取数据的函数
function foo(url, func) {
let isFirst = true
const ifr = document.createElement('iframe')
loadFunc = () => {
if (isFirst) {
// 设置为同源
ifr.contentWindow.location = 'about:blank'
isFirst = false
} else {
func(ifr.contentWindow.name)
ifr.contentWindow.close()
document.body.removeChild(ifr)
}
}
ifr.src = url
ifr.style.display = 'none'
document.body.appendChild(ifr)
// 加载以后的回调
ifr.onload = loadFunc
}
foo(`http://127.0.0.1:5501/name.html`, (data) => {
console.log(data) //
})
----------------------------------------------
<!-- name.html -->
const obj = { name: "iframe" }
// 修改name的值,必须为string类型
window.name = JSON.stringify(obj);
复制代码
document.domain
的值对应当前页面的域名http://a.baidu.com
与http://b.baidu.com
这2个子域名之间才能使用domain跨域,通常用于子域名之间的跨域访问<!-- test.html -->
<script>
document.domain = 'baidu.com';
const ifr = document.createElement('iframe');
ifr.src = 'a.baidu.com/test.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 此处便可操做domain.html的document
ifr.onload = null;
};
</script>
----------------------------------------------
<!-- domain.html -->
<script>
// domain.html下设置为与test.html中的domain一致
document.domain = 'baidu.com';
</script>
复制代码
// nginx.conf配置
server {
listen 80; // 监听端口
server_name www.baidu.com; // 匹配来源
location / { //匹配路径
// 反向代理到http://127.0.0.1:3000
proxy_pass http://127.0.0.1:3000;
// 默认入口文件
index index.html index.htm index.jsp;
}
复制代码
setTimeout(func|code, [delay], [arg1], [arg2], ...)
参数1为想要执行的函数或代码字符串,参数2为延迟执行时间,单位毫秒默认0,参数3及以后的参数为参数1为函数时传入的参数,调用以后会返回一个定时器idclearTimeout(id)
清除定时器来取消回调也就是说setTimeout是在上一次回调执行以后才开启的定时
setInterval(func|code, [delay], [arg1], [arg2], ...)
,参数列表同setTimeout,参数2为每次循环时间因为这2个api都属于异步宏任务,在执行的时候都会进入任务队列,若是队列前的任务执行时间较长,那么也会影响到定时器的执行时机
在浏览器中alert、confirm、prompt都会阻塞js主线程执行,直到弹窗消失,可是定时器还会继续执行;定时器并不能达到0延迟,最小延迟限制在4ms
requestAnimationFrame(callback)
的参数就是每次渲染前须要执行的动画更新函数,当浏览器将要重绘画面时就会执行这个回调函数,这个回调函数接受一个参数,即从当前页面加载以后到如今所通过的毫秒数document.getElementById("btn").onclick = function () {}
----------------------------------------------
<input type="button" onclick="alert('hi!');">
复制代码
addEventListener(eventName,callback,isCapturing)
方法为元素设置事件监听器,参数1为注册事件名不带on开头的string类型,参数2为触发事件的回调函数,接受一个事件对象参数,参数3为是否在捕获阶段触发,默认为falseremoveEventListener(eventName,callback,isCapturing)
方法移除指定事件名、回调、是否捕获的事件,匿名回调没法删除<ul id="f">
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
<script>
const ul = document.querySelector('#f')
// 点击li时触发事件委托
ul.addEventListener('click',function foo(event){
// 处理元素为父元素
console.dir(event.currentTarget) // ul#f
// 触发元素为子元素,event.target为具体触发对象
console.dir(event.target) // li
})
//--------------------------------------------
// 经过点击添加子元素
ul.addEventListener('click',function foo(event){
const child = document.createElement('li')
child.innerText = '我是新增的子元素'
event.currentTarget.appendChild(child)
})
//--------------------------------------------
// 经过点击删除子元素
ul.addEventListener('click',function foo(event){
event.currentTarget.removeChild(event.target)
})
</script>
----------------------------------------------
<!-- 若是点击span 想知道是哪一个li下面的元素 -->
<ul id="f">
<li>a</li>
<li>
<span>b</span>
</li>
<li>
<span>c</span>
</li>
</ul>
<script>
const ul = document.querySelector('#f')
ul.addEventListener('click', function foo(event) {
let target = event.target
// 一级级向上寻找直到找到知足条件的元素
while (target.nodeName.toLowerCase() !== 'li') {
target.target.parentNode
}
console.dir(target) // li
console.dir(target.parentNode === event.currentTarget) //true
})
</script>
复制代码
以上总结可能没有什么顺序,可是每章节都是针对性的讲解,零散的知识点较多,但愿看完这篇文章能扩展你的知识面,也许某方面讲的不是很详细,若是感兴趣能够找些针对性的文章进行深刻了解。
部份内容并不是原创,仍是要感谢前辈的总结,若是本文影响到您的利益,那么还请事先告知,在写本文时的初衷就是想给更多学习前端的小伙伴拓展知识,夯实基础,共同进步,也为了之后方便复习使用
总结不易,如需转载请注明出处,感谢!
若是本文对你有所帮助,就请点个赞支持一下吧,让更多人看到,你的支持就是我坚持写做下去的动力,若是喜欢个人文章,那么还请关注后续的文章吧~ ψ(`∇´)ψ
本人正在构建的GitHub 前端知识体系 项目,记录整理的知识点,能够一块儿学习与分享!
部份内容非原创,若有须要,请原创做者联系本人添加连接!