JavaScript-cnblog
JavaScript
文章目录
- JavaScript
- 1. JavaScript 数据类型
- 2. Undefined出现场景
- 3. NUll类型
- 4. undefined与null的相同点和不同点
- 5. Boolean类型转换
- 6. Number类型
- 7. isNaN与Number.isNaN
- 8. String 类型
- 9. 字符串处理
- 10. 比较运算符
- 11. typeof运算符
- 12. 判断对象是否为空对象
- 13. 判断是否为空数组
- 14. 流程控制中switch的比较
- 15. 引用数据类型
- 16. new 关键字的原理
- 17. 判断对象的属性自己身上没有而原型身上有
- 18. Object.create的源码
- 19. 判断对象的属性是否可枚举
- 20 newObject的注意点
- 21. 模拟new操作符
- 21. 获取引用类型的原型名称
- 22 使用reduce实现统计数组中每个元素的出现次数
- 23. es6的扩展运算符实现最大值最小值
- 24. 手写find函数
- 25 手写filter函数
- 26. 手写some函数
- 27. every函数
- 28. 书写map函数
- 28. 手写reduce函数
- 29. 函数定义的三种方式
- 30. arguments对象
- 31. 构造函数与普通函数的区别
- 32. 作用域和作用域链
- 33 变量被赋值全局变量,啥也不干报错
- 34. 函数提升
- 35. 闭包
- 36.this指向
- 37 apply的使用场景
- 38 call的使用场景
- 39. 手写Call函数
- 40 手写apply
- 41. 手写bind
- 对象
- 42. 对象属性定义
- 43. 对象的创建
- 44. 深浅拷贝
- 45. 原型对象的重写
- 46. 继承
- 47. jQuery模拟实现
- 48. jQuery实现选择器
- 49. jQuery实现html
- 50 jQuery实现extend
- 51. extend 实现多个参数传递
- DOM
- 52. 选择器
- 注意点
- 53. HTMLCollection对象与NodeList对象
- 54. DOM操作
- 54 优化dom操作
- 55. 事件捕获阶段,目标阶段,冒泡阶段
- 56 事件对象
- 57 触发事件的目标对象
- 58 事件模型
- 59 事件委托
- 60 浏览器的重绘和重排
- AJAX
- 61 AJAX的基本使用
- 62. AJAX的优缺点
- 63. get和post请求
- 64. 浏览器的同源策略
- 65 跨域
- ES6
- 66 . let 与 var
- 67. 块级作用域
- 68. let暂时性死区
- 68. let 不允许重复声明
- 69. const
- 70. 解构赋值
- 71. 对象解构赋值
- 嵌套结构对象的解构
- 72. 解构赋值的好处
- 73. 扩展运算符(将数组分离成一个个参数)
- 73- rest运算符(将剩余参数组合成数组)
- 切割变量
- 74. 扩展运算符和剩余运算符的区分
- 75. 箭头函数
- 76. ES6中属性和方法的简写
- 77 浅拷贝
- 78 Symbol
- 79. Proxy
- 应用场景1---值的校验
- 应用场景2-vue3响应式原理
- 应用场景-实现私有属性
- 80 set
1. JavaScript 数据类型
- 普通数据类型:Undefined,Boolean,Number,NUll,String,Symbol(es6新增)
- 复杂数据类型:Function,Array,Object,Date(),Map,Set
2. Undefined出现场景
- 变量只声明,不初始化
- 获取一个对象不存在的属性
- 接收一个没有返回值的函数结果
- 函数的实参不传递或者实参个数小于形参
3. NUll类型
- 定义:一个空指针对象
- 出现场景
- 变量初始化,赋值为null
- 获取dom元素,获取不到为null
- 正则表达式匹配不到,返回为null
4. undefined与null的相同点和不同点
相同点
- 字面值都是只有一个,undefined–>undefined,null–>object, typeof 检测
- 转换为Boolean类型都是false----> null==undefined
- 不能转换成对象—>抛出typeError异常
不同点
null是一个关键字,而undefined是一个全局变量—>挂载在window对象或global对象上
数值转换 null+1=1,undefined+1=NaN
5. Boolean类型转换
数值型
- NaN,0—>false,其他true
String
- ‘’–>false,其他true
Function
- true
Object
- null—>false,其他true
6. Number类型
- 抛异常情况
var num=0xah h超过了16进制标识
类型转换(Number()函数)
- undefined—>NaN
- null—>0
- true—>1
- false—>0
- ‘’—>0
- ’ '—>0
- ‘12’—>12
- ‘012’—>12
- ‘0012’—>12
- ‘00.12’—>0.12
- ‘0x10’—>16
- ‘123abc’—>NaN
- object—>调用对象的valueof方法—>上面不行调用toString方法
parseInt函数
语法规则:parseInt(字符串,进制)
作用:解析一个字符串返回数字
如果传入不是字符串,则隐式转换成字符串再进行解析
‘abc’—>NaN
‘fg123’,16—>15(前缀匹配,f满足16进制声明,后面g第一个不匹配,直接舍弃)
12—>12
‘12*2’—>12
12*2—>24
‘12.97’—>12
与map连用的注意点
let arr=['1','2','3','4']let result=arr.map(parseInt)// 等价于
let result=arr.map((item,index)=>{parseInt(item,index) // 这里第二个参数出现问题'1',0--->1 原样返回'2',1---->NaN 不符合规范'3',2---->NaN'4',3---->NaN
})
parseFloat
字符串解析成浮点数
参数只有一个,无进制概念
前置匹配
parseFloat(' 2.3')---->2.3
'12.23.33'----->12.23
-----
无进制概念
'fg123'---->NaN
7. isNaN与Number.isNaN
NaN和谁都不相等
isNaN:判断一个值是否能被转换成数字类型(es5)
- 其中存在隐式转换的过程
- ‘abc’–>NaN true
- ‘undefined’—>NaN true
- NaN—>Nan true
- {}---->NaN true
Number.isNaN:直接判断值是否为NaN(es6添加)
- 只有NaN才会返回true,解除了二义性
8. String 类型
创建字符串的方式
- 字面量创建:let str=‘123’
- String() :let str=String(‘123’)
- new String():let str=new String(‘123’)
区别
- 字面量创建和String()创建的是基本字符串,比较时只比较值
- new String()创建的返回的一个对象实例,比较时比较的是内存地址
String实例对象特性
- 原型链上由许多方法
- 基本字符串也可以调用(为什么)
- 调用是隐式转换(自动装配成String包装类型)
9. 字符串处理
- 字符串转数组—> arr.split(“”)
- 数组翻转:arr.reserve()
- 字符串下标字符:str.chatAt(i)
- 字符串下标ascall:str.charAtCode(i)
- 下标的值,str.indexOf(‘char’)
- 倒着来下标的值,str.lastIndexOf(‘char’)
10. 比较运算符
三等于运算符
- 先比较值的类型是否相同
- 再比较值是否相同
// Number存在类型转换
1===Number(1) true// new出来的是基本数据类型的包装类型
1===new Number(1) false//
'hello'===String('hello') // 都是基本类型true
双等于运算符
- 比较更加复杂
- 先比较值的类型是否相同
- 不相同则进行隐式类型转换,转换为相同的类型,再进行值的比较
隐式转换规则
- 字符串和数值—>字符串转换为数值
- 一方为boolean类型,boolean类型转换为数值
11. typeof运算符
type of class :注意是function
typeof 6/2 :NaN
typeof (6/2):number
12. 判断对象是否为空对象
JSON.stringify(obj)===‘’
hasOwnProperty
let obj={}function isEmpty(obj){for(key of obj){if(obj.hasOwnProperty(key)){return false}}return true
}
13. 判断是否为空数组
arr instanceof Array && arr.length<=0
14. 流程控制中switch的比较
- 为===比较
15. 引用数据类型
- 堆内存,栈内存存放地址
- 通过new关键字创建
16. new 关键字的原理
function Person(username,sex){this.username=usernamethis.sex=sex// 这里默认return this
}// 等价于// new 关键字做的事情var person={}person.__proto__=Person.prototypePerson.call(person)
17. 判断对象的属性自己身上没有而原型身上有
function propertyInPrototype(obj,pro){return !obj.hasOwnProperty(pro)&& pro in obj
}
18. Object.create的源码
- 用于寄生继承(创建一个空对象,对象的原型指向要继承的原型)
Object.create=function(proto,protoProperty){// 构造函数function F(){}F.prototype=proto// 第二个参数一个对象if(protoProperty){Object.defineProperties(F, protoProperty)}return new F()}
19. 判断对象的属性是否可枚举
console.log(stu.propertyIsEnumerable("sayHello")); // false :sayHello属于原型上的函数//将userName属性设置为不可枚举Object.defineProperty(stu, "userName", {enumerable: false,});
20 newObject的注意点
const a={age:16}
const b=new Object(a)
b===a // true
21. 模拟new操作符
function Person(name,age){this.name=namethis.age=age}Person.prototype.sayHi=function(){console.log('666')
}function New(){var obj={}Person.call(obj,arguments)obj.__proto__=Person.prototypereturn obj
}New("name",'18').sayHi()
21. 获取引用类型的原型名称
instanceof
var arr=[]arr instanceof Array
判断它最近一层的构造函数
var arr=[]arr.__proto__.constructor === Array
使用Object的toString方法
var arr=[]Object.prototype.toString.call(arr)
兼容性解决isArray方法
if(!Array.isArray){Array.isArray=function(args){return Object.prototype.toString().call(args)==='[object Array]'}
}
22 使用reduce实现统计数组中每个元素的出现次数
function getCount(arr){return arr.reduce((pre,next)=>{pre[next]? pre[next]++:(pre[next]=1)return pre},{})
}
23. es6的扩展运算符实现最大值最小值
var arr=[1,3,6,9,20]Math.min(...arr) // 获取最小值
Math.max(...arr) // 获取最小值
24. 手写find函数
Array.prototype.myFind=function(callback){// 判断callback是否为函数if(typeof callback !=='function'){throw new Error('参数不是函数')}// this为方法调用者,也就是数组本身let length=this.lengthfor(let i=0;i<length;i++){let res=callback.call(this,this[i],i)if(res){// 找到了return this[i]}}// 找不到return undefined}
25 手写filter函数
Array.prototype.myFilter=function(callback){// 判断callback是否为函数if(typeof callback !=='function'){throw new Error('参数不是函数')}let length=this.length// 返回值为一个新的数组let arr=[]for(let i=0;i<length;i++){let res=callback.call(this,this[i],i)if(res){// 满足条件--->加入数组arr.push(this[i])}}return arr
}
26. 手写some函数
Array.prototype.myFilter=function(callback){// 判断callback是否为函数if(typeof callback !=='function'){throw new Error('参数不是函数')}let length=this.length// 返回值为一个boolean值let flag=falsefor(let i=0;i<length;i++){let res=callback.call(this,this[i],i)if(res){// 满足条件--->加入数组flag=truebreak;}}return flag}
27. every函数
- 和some基本一致
28. 书写map函数
Array.prototype.myFilter=function(callback){// 判断callback是否为函数if(typeof callback !=='function'){throw new Error('参数不是函数')}let length=this.length// 返回值为一个新的数组let newArr=[]for(let i=0;i<length;i++){// 注意这里为修改后的对象let res=callback.call(this,this[i],i)// 我们需要进行深拷贝解除引用的关系// 我们对象才进行深拷贝if(typeof res==='object'){let newItem=JSON.parse(JSON.stringify(res))}newArr.push(newItem)}return newArr}
28. 手写reduce函数
Array.prototype.myFilter=function(callback,initValue){// 判断callback是否为函数if(typeof callback !=='function'){throw new Error('参数不是函数')}let length=this.length// 判断数组长度if(length<=0){// 空数组if(initValue){return initValue}else{return undefined}}let i=0// 判断是否存在初始值if(!initValue){initValue=this[i]i++}for(i;i<length;i++){// 注意这里为新的initValueinitValue=callback.call(this,initValue,this[i],i)}return initValue}
29. 函数定义的三种方式
- 函数声明
- 函数表达式
- 构造函数
// 构造函数的形式,最后一个参数为函数体
var fn=new Function('num1','num2','return num1+num2')
构造函数创建函数的特点
- 效率较低
- 作用域为顶级作用域
var a=12function fn(){var a=11return new Function('return a')
}fn()() // 结果为12 为顶级作用域的a
30. arguments对象
argument只存在于函数作用域中
结构是伪数组,可通过下标访问
内部添加argument通过下标添加,不会发生变化
应用
- 可以判断参数个数
- 可以实现方法重载,参数可以不同,但都可以通过arguments对象获取
31. 构造函数与普通函数的区别
- 构造函数一般首字母大写
- 构造函数需要配合new使用
- 构造函数内部可以使用this指向挂载
- 构造函数的执行过程(this执向,默认返回this)
32. 作用域和作用域链
- 作用域:一个变量定义的调用的范围
- 作用域链,查找变量时先查找当前作用域,找不到就往外层作用域进行查找,查找的过程就形成了作用域链
33 变量被赋值全局变量,啥也不干报错
// 报错
(function(){console.log(str)str='hello world'
})()// hello world
(function(){str='hello world'console.log(str)})()
34. 函数提升
- 特殊例题
var a=true
foo()
function foo(){if(a){var a=20}console.log(a)
}// 结果为undefined// 分析程序等价于
function foo(){var a;if(a){a=20}console.log(a)
}
function foo(){var a=1function b(){a=10returnfunction a(){}}b()console.log(a)
}
foo()// 1// 代码等价于
function foo(){var a;function b(){function a(){}a=10return}a=1b()console.log(a) // a先找自己作用域a,b里面的a是全局作用域的
}
35. 闭包
- 可以读取外部变量的函数
出现原因
- 内部函数维持了对外部变量的引用
应用场景
变量私有:只能是内部方法(维持对外部作用域变量的引用)进行调用修改其值
解决延时器的问题,var循环通过立即执行函数生成闭包,维持对外部作用域的变量(形参)的引用
特殊题,注意this指向和闭包
var userName = "zhangsan";var person = {userName: "lisi",method: function () {return function () {return this.userName;};},};console.log(person.method()()); //zhangsan
- 闭包+作用域(从函数定义区域向外找闭包)
function create() {var a = 100;return function () {console.log(a);};}var fn = create();var a = 200;fn(); // 100
function print(fn) {var a = 200;fn();}var a = 100;function fn() {console.log(a); // 100}print(fn);
复杂的一道题
var num = 10; // window.num=10var obj = { num: 20 }; // obj.num=20obj.fn = (function (num) {this.num = num * 3; // 立即执行 window.num=60num++; // 立即执行 obj.num=21return function (n) {this.num += n; // fn(5) wind.num=65 // fn(10) obj.num=30num++; // fn(5) obj.num=22 // fn(10) 23console.log(num); // fn(5) 22 //fn(10)23};})(obj.num);var fn = obj.fn;fn(5);obj.fn(10);console.log(num, obj.num);
36.this指向
- 闭包+this指向
<button id="btn">获取用户信息</button><script>var userInfo = {data: [{ userName: "zhangsan", age: 20 },{ userName: "lisi", age: 21 },],getUserInfo: function () {this.data.forEach(function (p) {// 这里的this,会进行作用域的查找,一直往外找,直到找到this(window)// forEach的源码,是不是对传入的参数,没有进行this指向的绑定,否则this将会是this.dataconsole.log(this);});},};var btn = document.getElementById("btn");// btn.onclick = userInfo.getUserInfo;btn.onclick = userInfo.getUserInfo.bind(userInfo);</script>
- 处理方法,使用that保存this
- 使用箭头函数
37 apply的使用场景
- apply的传递参数为一个数组,可以改变原函数的传参方式
// 如Math.max方法// 原本的调用方法
Math.max(1,2,3,4,5,6)// 修改为数组传参的方式
Math.max.apply(null,[1,2,3,4,5,6])
38 call的使用场景
- 伪数组转数组
function fn(){let arr=Array.prototype.slice.call(arguments)
}
组合继承时改变this指向
立即执行函数时的this执行
var person = [{ id: 1, userName: "zhangsan" },{ id: 2, userName: "lisi" },];for (var i = 0; i < person.length; i++) {(function (i) {// 这里的this指向windowthis.print = function () {console.log(this.id);};this.print();})(i);}// 要想实现打印person中对象的值,需要修改立即执行函数中的this(function(i){}).call(person[i],i)
39. 手写Call函数
// 原理,在传递进来的this指向上(挂载一个方法)(这个方法为call函数调用者,即代替调用,实现this指向修改)Add.call(sub,1,2,3)Function.prototype.myCall=function(context){// 获取除this外的所有参数let newArguments=Array.prototype.slice.call(arguments,1)// call函数的调用者(是一个函数)let startPerson=this// 新的this指向let newThis=context||window // 没传,默认window// 我们用新的this指向 调用函数(startPerson)newThis.fn=startPersonreturn newThis.fn(...newArguments)
}
40 手写apply
// 原理,在传递进来的this指向上(挂载一个方法)(这个方法为call函数调用者,即代替调用,实现this指向修改)Add.call(sub,1,2,3)Function.prototype.myCall=function(context){// call函数的调用者(是一个函数)let startPerson=this// 新的this指向let newThis=context||window // 没传,默认window// 我们用新的this指向 调用函数(startPerson)newThis.fn=startPerson// 和call不同的地方,apply只有两个参数if(arguments[1]){// 第二个参数return newThis.fn(...arguments[1])}else{return newThis.fn()}}
41. 手写bind
// 只返回一个函数,不调用Function.prototype.myBind=function(context){// 参数传递与call相同// 获取除this外的所有参数let newArguments=Array.prototype.slice.call(arguments,1)// call函数的调用者(是一个函数)let startPerson=this// 新的this指向let newThis=context||window// 返回值为一个函数return function(){// 这里还有一个问题,返回的函数调用时,如果传递参数// 是不是要加进去let bindArguments=Array.prototype.slice.call(arguments)// 这个函数一执行,就调用apply函数return startPerson.apply(newThis,newArguments.concat(bindArguments))}
}
对象
42. 对象属性定义
- Object.defineProperty
定义某个属性的特性
// 使属性的值变得不可修改let person={}Object.defineProperty(person,"age",{writable:false
})
定义对象的get和set方法
// 定义私有变量,不想让外界直接修改,提供get和set方法进行访问
let person={_age:18
}Object.defineProperty(person,"age",{get:function(){return this._age},set:function(newValue){this._age=newValue}
})
43. 对象的创建
- 字面量
- 工厂函数
- 问题:没有原型,大家都是Object
- 希望的结果,Person数据类型,Teacher数据类型
function factory(){let o= new Object()return 0
}
- 构造函数
- 原型上挂载一类对象的公共属性和方法(原型函数)
- 类
44. 深浅拷贝
浅拷贝:拷贝前后值类型互不影响,引用类型会受到影响
function shallowCopy(src){let dst={}// 这里会遍历到继承的属性for(let val in src){if(src.hasOwnProperty(val)){dst[val]=src[val]}}return dst
}
- 使用Object.assign实现对象的浅拷贝
Object.assign(dst,src) // 注意这里拷贝的是对象的可枚举的类型变量
深拷贝:在浅拷贝的基础上,对引用数据类型的拷贝,拷贝前后互不影响
- 使用JSON.stringify 和 JSON.parse
// 这种方法存在的问题
// 1. 拷贝时:属性是函数是,新对象没有改函数属性
// 2. 新的对象的原型变成了Object
// 3. 对象的属性存在循环引用时,会抛异常
- 自己手写一个解决上述问题
let map=new WeakMap()
function deepCopy(src){// 基本思路,如果属性是引用类型,继续递归调用改函数// 如果是基本数据类型,返回原来的值// 其实可以继续完善,如Array单独处理// obj类型我们就通过__proto__.constructor.name获取原型类// 对创建的{} 我们让其__proto__ 为 类的prototype// 循环引用的问题,存储一个键值对,键位prop属性名,值为属性值if(typeof src==='object'){let newObj=Array.isArray(src)? []:{}// 遍历对象的所有属性,递归调用deepCopy// 判断是否循环引用if(map.get(src)){return src}// 存储引用// 其实就是看这个引用是否地址是否已经被创建// 如果已经创建,则让拷贝的新对象(某个属性)直接指向即可// 没有创建,也就是新对象某个属性的值(需要重新创建引用)map.set(src,newObj)for(prop of src){//newObj[prop]=deepCopy(src[prop])}return newObj}else{return src}
}
45. 原型对象的重写
- 如果我们想在一个原型上同时添加属性和方法,又不想写多行代码,我们直接赋值一个对象
- 问题就是原型关系被破坏
- 加上constructor属性解决
function Person() {}Person.prototype = {constructor: Person, //添加constructoruserName: "zhangsan",age: 20,sayHi: function () {console.log(this.userName);},};var person = new Person();person.sayHi();console.log(Person.prototype.constructor);// Person
46. 继承
- 原型链继承
//1. 优点:实现简单,子类的实例可以访问原型链上的所有属性和方法// 2. 缺点// 所有实例将共享父类的的属性和方法// 如果父类的构造函数有参数,参数无法传递 new Student('123')
- 构造函数继承
// Person.call(this)//1. 优点 解决子类向父类传递参数// 2. 子类不能访问父类的原型方法(原型链没有改变)
- 拷贝继承
// 把父类的属性和方法都赋值给子类// 1. 首先判断属性和方法在原型上还是在实例上
// 2. 根据位置的不同,赋值给子类的地方也不同function Person(age){this.age=agethis.sayHi=function(){...}
}Person.prototype.run=function(){}// 子类拷贝属性和方法
// 构造方法参数按需传入function Student(id,age){let person=new Person()for(let key of person){if(person.hasOwnProperty(key)){this[key]=person[key]}else{// 原型上Student.propertype[key]=person[key]}}this.id=id
} // 优点,可以继承属性和方法,且互不干扰
// 可以向父类传递参数// 本质是没有修改原型链,而是手写一个相同的类除了名字不同,// 本来是重写一遍,现在用代码实现了循环写
- 组合继承
- 构造函数继承+原型链继承
问题是子类的实例的__proto__为父类的一个实例,要父类的方法,这个父类的实例只是一个跳板,并无实际作用// 也就是,我们一个创建一个空对象,让该对象的__proto__指向父类即可
// 也就是寄生组合继承
- 寄生组合继承
// 定义Super构造函数function Super() {}
//Super.prototype原型对象指向了Person.prototypeSuper.prototype = Person.prototype;
//Student.prototype原型对象指向了Super的实例,这样就去掉了Person父类的实例属性。Studnet.prototype = new Super();Studnet.prototype.constructor = Studnet;var student = new Studnet(1001, 21);
- 或者使用Object.create(Person) 创建一个对象,该对象的原型为Person
- 即
obj.__proto__=Person.prototype
- 即
47. jQuery模拟实现
var $=(jQuery=function(){return jQuery.fn.init() // 返回jQuery原型
})jQuery.fn=jQuery.prototype={init:function(){return this // 这个this即为jQuery.prototype},verson:"7.1",length:1,size:function(){return this.length}}$().version
- 作用域污染问题
- this.length=0 会使原型上的length=0
var $=(jQuery=function(){return jQuery.fn.init() // 返回jQuery原型
})jQuery.fn=jQuery.prototype={init:function(){this.length=0 return this // 这个this即为jQuery.prototype},verson:"7.1",length:1,size:function(){return this.length}}$().version
- 解决,将init方法返回一个实例(实例的
__proto__
为原型),而不是原型
var $=(jQuery=function(){return new jQuery.fn.init() // 返回jQuery原型
})jQuery.fn=jQuery.prototype={init:function(){this.length=0 this._size=function(){return this.length}},verson:"7.1",length:1,size:function(){return this.length}}// 原型链修改
jQuery.fn.init.prototype=JQuery.fn$().version
48. jQuery实现选择器
// selector 选择器
// context 上下文
var $=(jQuery=function(selector,context){return new jQuery.fn.init() // 返回jQuery原型
})jQuery.fn=jQuery.prototype={init:function(selector,context){// 设置默认值selector=selector||documentcontext=context||documentif(selector.nodeType){// 是DOM元素this[0]=selectorthis.length=1this.context=selectorreturn this}if(typeof selector==='string'){// 字符串// 这里应该使用正则表达式判断各种选择器//如果选择器是一个字符串var e = context.getElementsByTagName(selector); // 获取指定名称的元素//通过for循环将所有元素存储到当前的实例中for (var i = 0; i < e.length; i++) {this[i] = e[i];}this.length = e.length; //存储元素的个数this.context = context; //保存上下文对象return this; //返回当前的实例}else{// 空this.length = 0;this.context = context;return this;}},verson:"7.1",length:1,size:function(){return this.length}}// 原型链修改
jQuery.fn.init.prototype=JQuery.fn$().version
49. jQuery实现html
// selector 选择器
// context 上下文
var $=(jQuery=function(selector,context){return new jQuery.fn.init() // 返回jQuery原型
})jQuery.fn=jQuery.prototype={init:function(selector,context){// 设置默认值selector=selector||documentcontext=context||documentif(selector.nodeType){// 是DOM元素this[0]=selectorthis.length=1this.context=selectorreturn this}if(typeof selector==='string'){// 字符串// 这里应该使用正则表达式判断各种选择器//如果选择器是一个字符串var e = context.getElementsByTagName(selector); // 获取指定名称的元素//通过for循环将所有元素存储到当前的实例中for (var i = 0; i < e.length; i++) {this[i] = e[i];}this.length = e.length; //存储元素的个数this.context = context; //保存上下文对象return this; //返回当前的实例}else{// 空this.length = 0;this.context = context;return this;}},// 原型上挂载方法html:function(val){// 是不是要把选择器选中的所有元素都添加同样的内容jQuery.each(this,function(val){this.innerHtml=val},val)}verson:"7.1",length:1,size:function(){return this.length}}// 实现隐式迭代html的方法
jQuery.each=function(object,callback,agrs){for(let i=0;i<object.length;i++){callback.call(object[i],args)}return object
}// 原型链修改
jQuery.fn.init.prototype=JQuery.fn$().version$().html("<h1>666</h1>")
50 jQuery实现extend
// selector 选择器
// context 上下文
var $=(jQuery=function(selector,context){return new jQuery.fn.init() // 返回jQuery原型
})jQuery.fn=jQuery.prototype={init:function(selector,context){// 设置默认值selector=selector||documentcontext=context||documentif(selector.nodeType){// 是DOM元素this[0]=selectorthis.length=1this.context=selectorreturn this}if(typeof selector==='string'){// 字符串// 这里应该使用正则表达式判断各种选择器//如果选择器是一个字符串var e = context.getElementsByTagName(selector); // 获取指定名称的元素//通过for循环将所有元素存储到当前的实例中for (var i = 0; i < e.length; i++) {this[i] = e[i];}this.length = e.length; //存储元素的个数this.context = context; //保存上下文对象return this; //返回当前的实例}else{// 空this.length = 0;this.context = context;return this;}},// 原型上挂载方法html:function(val){// 是不是要把选择器选中的所有元素都添加同样的内容jQuery.each(this,function(val){this.innerHtml=val},val)}verson:"7.1",length:1,size:function(){return this.length}}// 实现隐式迭代html的方法
jQuery.each=function(object,callback,agrs){for(let i=0;i<object.length;i++){callback.call(object[i],args)}return object
}// 原型链修改
jQuery.fn.init.prototype=JQuery.fn// 在原型上添加扩展的方法
jQuery.extend=jQuery.fn.extend=function(obj){for(let prop in obj){this[prop]=obj[prop]}return this
}// 调用
jQuery.fn.extend({text:function(val){jQuery.each(this,function(val){this.innerText=val},val)}
})$().version$().html("<h1>666</h1>")
51. extend 实现多个参数传递
// selector 选择器
// context 上下文
var $=(jQuery=function(selector,context){return new jQuery.fn.init() // 返回jQuery原型
})jQuery.fn=jQuery.prototype={init:function(selector,context){// 设置默认值selector=selector||documentcontext=context||documentif(selector.nodeType){// 是DOM元素this[0]=selectorthis.length=1this.context=selectorreturn this}if(typeof selector==='string'){// 字符串// 这里应该使用正则表达式判断各种选择器//如果选择器是一个字符串var e = context.getElementsByTagName(selector); // 获取指定名称的元素//通过for循环将所有元素存储到当前的实例中for (var i = 0; i < e.length; i++) {this[i] = e[i];}this.length = e.length; //存储元素的个数this.context = context; //保存上下文对象return this; //返回当前的实例}else{// 空this.length = 0;this.context = context;return this;}},// 原型上挂载方法html:function(val){// 是不是要把选择器选中的所有元素都添加同样的内容jQuery.each(this,function(val){this.innerHtml=val},val)}verson:"7.1",length:1,size:function(){return this.length}}// 实现隐式迭代html的方法
jQuery.each=function(object,callback,agrs){for(let i=0;i<object.length;i++){callback.call(object[i],args)}return object
}// 原型链修改
jQuery.fn.init.prototype=JQuery.fn// 在原型上添加扩展的方法
jQuery.extend=jQuery.fn.extend=function(obj){// 多个参数的处理if(typeof arguments[0]==='string'&&typeof arguments[1]==='string'){var destination = arguments[0],source = arguments[1];//把第二个对象合并到第一个参数对象中,并返回合并后的对象for (var property in source) {destination[property] = source[property];}return destination}else{for(let prop in obj){this[prop]=obj[prop]}return this}}// 调用
jQuery.fn.extend({text:function(val){jQuery.each(this,function(val){this.innerText=val},val)}
})// 实现样式的修改
jQuery.fn.extend({fontStyle:function(obj){var defaults = {color: "#ccc",size: "16px",};//如果有参数,会覆盖掉默认的参数defaults = jQuery.extend(defaults, obj || {});//为每个DOM元素执设置样式.jQuery.each(this, function () {this.style.color = defaults.color;this.style.fontSize = defaults.size;});}
})$().version$().html("<h1>666</h1>")
DOM
52. 选择器
<body><div><h4>标题内容</h4><span>span标签内容</span><p>段落内容<span>段落中的第一个span标签</span><br /><span>段落中的第二个span标签</span></p></div></body><script>console.log(document.querySelector("p span").innerHTML);// 获取p标签中第一个span标签中的内容,所以输出结果为:段落中的第一个span标签console.log(document.querySelector("h4,span").innerHTML);//获取第一个h4或者是span元素的内容:所以输出结果为:标题内容var ele = document.querySelector("p");console.log(ele.querySelector("div span").innerHTML);//段落中的第一个span标签。// 首先先找到`p`元素,然后看一下p元素下面有没有div,我们发现没有,但是依然能够匹配到span元素。//原因是:在匹配的过程中会优先找出最外层div元素下的span元素的集合,然后在判断span元素是否属于p元素的子元素,最后返回//第一个匹配到的span元素的值。</script>
注意点
- CSS选择器是先从目标元素开始查询,然后再筛选出满足条件的元素,所有有些结果让我们难以理解
var ele = document.querySelector("p");console.log(ele.querySelector("div span").innerHTML);//段落中的第一个span标签。// 首先先找到`p`元素,然后看一下p元素下面有没有div,我们发现没有,但是依然能够匹配到span元素。//原因是:在匹配的过程中会优先找出最外层div元素下的span元素的集合,然后在判断span元素是否属于p元素的子元素,最后返回//第一个匹配到的span元素的值。
53. HTMLCollection对象与NodeList对象
HTMLCollection
- 具有length对象
- 可以通过item()和namedItem()函数访问特定元素
<div id="container"><div class="bar"></div><div class="foo"><div class="inner"></div></div></div>
<script>var main = document.getElementById("container").children;console.log(main); //HTMLCollectionconsole.log(main.item(0)); //输出:<div class="bar"></div>console.log(main.item(1)); // 输出:foo元素
</script>
- namedItem根据元素上的name值获取
<form id="form1"><input type="text" id="userName" /><input type="password" id="password" name="userPwd" /></form>
<script>var form1 = document.getElementById("form1").children;console.log(form1.namedItem("userPwd"));// <input type="password" id="password" name="userPwd" />
</script>
NodeList
- 具有length属性
- item方法
相同点
- 结构都是伪数组,转换为真正的数组
Array.prototype.slice.call(htmlCollection||NodeList)
不同点
- NodeList存储多种节点,文本,注释,元素节点
- 而HTMLCollection只有元素节点
- children获取HTMLCollection
- childNodes获取NodeList
54. DOM操作
- 一些不常见的操作
- 创建属性:
createAttribute("type")
- 设置属性节点:
newInput.setAttributeNode(newAttr);
- 设置文本节点:
var newTextNode = document.createTextNode("用户密码"); form1.appendChild(newTextNode); //添加文本节点
- 创建属性:
//创建一个input元素var newInput = document.createElement("input");//创建属性var newAttr = document.createAttribute("type");newAttr.value = "password";//将属性绑定到元素上newInput.setAttributeNode(newAttr);//创建一个文本节点var newTextNode = document.createTextNode("用户密码");form1.appendChild(newTextNode); //添加文本节点form1.appendChild(newInput);
- 删除属性操作:
removeAttribute
<form id="form1">用户名<input type="text" id="userName" /> <br />用户密码<input type="password" id="password" name="userPwd" /></form><script>var input = document.querySelector("#userName");input.removeAttribute("id");
</script>
修改元素节点
- 元素替换
var father=document.querySelector("#container")// 将father中的div标签替换为p标签var div=father.querySelector("div")var newP=document.createElement("p")// 元素节点替换
father.replaceChild(newP,div)
54 优化dom操作
- 使用文档碎片
let ul=document.querySelector("ul")let frag=document.createDocumentFragment()for(let i=0;i<100;i++){document.createElement("li")li.innerHtml=`item{i}`// 插入到文档碎片frag.appendChild(li)
}ul.appendChild(frag)
55. 事件捕获阶段,目标阶段,冒泡阶段
事件捕获
- 从根节点开始,事件向内传播,直到目标节点
事件冒泡
- 从目标节点开始,事件向外传播,直到根节点
设置事件的传播顺序
xxx.addEventListen("click",function(){},true)
:事件以捕获方式传播第三个参数设置为flase,以冒泡方式传播,默认为false
阻止事件的冒泡
- event.stopPropagation():只阻止事件冒泡
- event.stopImmediatePropagation():不仅阻止事件冒泡,还阻止绑定在当前元素上的其他事件的执行
56 事件对象
- 形参 :event
- window.event
// 兼容性处理return event||window.event
57 触发事件的目标对象
// IE event.srcElement
// 其他event.taget 或者 同时都可以
return event.target||event.srcElement
58 事件模型
DOM0
- 一个函数赋值一个事件处理函数
<button onclick="fn()">点击</button>// 或者
let btn=document.querySelector("button")
btn.onclick=function(){}
- 缺点,一个事件(如click触发时),只有一个事件处理函数有效,后绑定生效
DOM2
- 不同浏览器厂商制定的不同的事件绑定方式
// IE 10以下// 绑定
element.attachEvent('on'+eventName,handler)
// 取消绑定
element.detachEvent('on'+eventName,handler)// 其他浏览器或者IE10+addEventListener(eventName,handler,useCapture) //添加事件处理程序
removeEventListener(eventName,handler,useCapture) // 删除事件处理程序
- 特性:一个事件可以绑定多个事件处理函数
- 移除绑定时不能使用匿名函数,需要绑定和移除时传入相同的函数
- addEventListen
- 多个事件处理函数顺序执行
- this指向绑定的元素
- attachEventListen
- 多个事件处理函数倒序指向
- this指向window
浏览器兼容性处理
let eventHandle={addEventListener:function(ele,type,handler){if(ele.addEventListener){ele.addEventListener(type,handler)}else if(ele.attachEvent){ele.attachEvent("on"+type,handler)}else{ele["on"+type]=handler}}
}
DOM3
- 允许自定义事件
var customeEvent//立即执行函数,隔离作用域
(function(){// 判断是否支持dom3事件模型if(document.implemention.hasFeature("CustomEvents","3.0")){let user={userName:"zhangsan"}// 创建一个事件customeEvent=document.createEvent("CustomEvent")// 初始化一个事件,事件名,是否冒泡,是否可以被取消,e.detail绑定值customeEvent.initCustomEvent("myEvent",true,false,user)}
})()// div盒子监听自定义事件
div.addEventListener("myEvent",function(e){// 初始化时user对象的属性被写入e.detail中console.log(e.detail.userName)
})// 一个按钮实现自定义事件的触发
btn.addEventListener("click",function(){div.dispatch(customeEvent)
})
59 事件委托
- 基于事件冒泡的机制,将本应注册到子元素的事件处理函数注册到父元素上
- 父元素对子元素的触发进行统一处理
60 浏览器的重绘和重排
浏览器渲染HTML的过程
- HTML代码被解析成DOM树,CSS代码被解析成样式规则集
- DOM树和样式规则集合并形成渲染树
- 根据渲染树进行节点的属性计算,如位置,大小,颜色
- 进行节点的渲染
重排
- 更改页面布局的一种操作
重排的发生场景
- 第一次渲染
- 浏览器窗口大小变化
- 元素的大小改变,影响周围
- 元素的增删
重排的发生机制
浏览器维护一个重排队列,里面存放一定量引起重排操作的样式改变,到达一定程度后统一进行一次重排
强制重排的情况
- 获取元素的样式
clicent系列 offset系列 scroll系列 width,height
重绘
- 改变元素在页面中的显示样式
重绘与冲重排的关系
- 重排一定引发重绘,而重绘不一定引发重排
减少重绘和重排的操作
- 统一添加样式,添加类
- 少使用table布局,多次引发重排
- 对一个元素需要进行复杂的样式添加时,先隐藏改元素,添加号后在显示
- 文档碎片
- 事件委派
AJAX
61 AJAX的基本使用
// 兼容性处理
let xhr=null
if(window.XMLHttpRequest){xhr=new XMLHttpRequest()
}else{xhr=new ActiveXObject("Microsoft.XMLHTTP")
}// 开始发起请求
// 第三个参数为是否为异步
xhr.open("post","xxx.url",true)// post请求需要设置请求头
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")xhr.open("name=zs&age=18")// 监听结果
xhr.onreadystatechange=function(){if(xhr.readyState===4&&xhr.status===200){console.log(xhr.responseText)}
}// readyState
// 0 初始化,未发送
// 1 open 未send
// 2 send 未响应
// 3.接收,响应部分
// 4.完成
62. AJAX的优缺点
优点
- 异步,无刷新请求
- 前后端分离
缺点
- 不利于SEO,页面内容JavaScript动态生成
- 破环了统一资源定位符的效果
- 相同的url地址,不同的人看到的效果不同
63. get和post请求
get
- 请求参数在地址栏
- 参数的大小有限制
post
- 传输大数据
- 参数在请求体,不在地址栏
64. 浏览器的同源策略
禁止不同页面的dom操作
- 防止iframe跨域,恶意网站嵌套正规网站,恶意网站操作正规网站的dom,获取用户信息
禁止XHR实现不同源的页面请求
- CSRF攻击:跨站请求伪造
- 恶意网站发起请求时,获取浏览器上存储的cookie,用这个cookie来去请求正规网站,正规网站返回信息被恶意利用
- CSRF攻击:跨站请求伪造
65 跨域
- cors跨域
- jsonp跨域
- 只支持get请求
- 在url的参数中添加回调函数,返回的结果为回调函数的执行
// 前端
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><script>window.onload = function () {var btn = document.getElementById("btnLogin");btn.addEventListener("click", function () {sendRequest();});};function sendRequest() {var userName = document.getElementById("userName").value;//请求参数,其中包含回调函数var param = "name=" + userName + "&callback=successFn";//请求的urlvar url = "http://localhost:3000/getUserNameInfo?" + param;var script = document.createElement("script");script.src = url;document.body.appendChild(script);}function successFn(result) {console.log("result=", result);}</script></head><body>用户名:<input type="text" id="userName" /> <br /><button id="btnLogin">登录</button></body>
</html>
- 后端
var express = require('express')
var app = express();
// app.all('*', function (req, res) {
// //设置可以接收请求的域名
// res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
// res.header('Access-Control-Allow-Methods', 'GET, POST,PUT');
// res.header('Access-Control-Allow-Headers', 'Content-Type');
// res.header('Content-Type', 'application/json;charset=utf-8');
// req.next();
// })
app.get('/getUserNameInfo', function (req, res) {var userName = req.query.name;//获取请求的回调函数var callbackFn = req.query.callbackconsole.log('callbackFn==',callbackFn)console.log('userName=',userName)var result = {id: 10001,userName: userName,userAge:21};var data = JSON.stringify(result);res.writeHead(200, { 'Content-type': 'application/json' })//返回值是对对回调函数的调用res.write(callbackFn+'('+data+')')// res.write(data);res.end()
})
app.listen(3000, function () {console.log('服务端启动....')
})
ES6
66 . let 与 var
let
- 不存在变量提升
- 块级作用域(大括号包裹的代码块)
var
- 存在变量提升
- 只有全局作用域和函数作用域
var引发的问题
var temp = new Date();function show() {console.log("temp=", temp)if (false) {var temp = "hello world";}}show();
- 输出undefined,temp变量提升导致
- if(false) 里面是块级作用域,而var不认识这个
67. 块级作用域
- 直接使用块级作用域
{let temp='xxx'console.log(temp)
}
- 之前使用立即执行函数创建独立(私有空间)
(function(){var temp='xxx'console.log(temp)
})()
- 循环打印
for (var i = 0; i < 3; i++) {setTimeout(function() {console.log('i=', i);}, 1000)}
- 立即执行函数
for (var i = 0; i < 3; i++) {(function(i){setTimeout(function() {console.log('i=', i);}, 1000)})(i)}
68. let暂时性死区
- 在区域内存在let声明,通过let声明的变量一开始就形成了一个封闭的作用域,在声明之前访问变量都是不被允许的
产生原因
- let不存在变量提升
68. let 不允许重复声明
- 错误示例
function test() {let num = 12;let num = 20;console.log(num)}test()
function test() {var num = 12;let num = 20;console.log(num)}test()
69. const
声明常量
不存在变量提升
块级作用域有效
暂时性死区
不允许重复声明
70. 解构赋值
let arr = [{userName: 'zs',age: 18},[1, 3], 6];let [{userName,age},[num1, num2], num3] = arr;console.log(userName, age, num1, num2, num3);
解构不成功,返回undefined
let [num1, num2] = [6]console.log(num1, num2); // num2=undefined
不完全解构
// 如果只取第一个值呢?let [num1] = [1, 2, 3];console.log(num1); //1//只取第二个值呢?let [, num, ] = [1, 2, 3];console.log(num); //2// 只取第三个值呢?let [, , num] = [1, 2, 3];console.log(num); //3
71. 对象解构赋值
let {userName: name,userAge: age} = {userName: 'ls',userAge: 20}console.log(name, age);// 将userName和userAge解构出来,并重命名为name和age
默认解构
let obj={username:'zhangsan'
}let {name,age=20}=objconsole.log(name,age)// age本来应该是undefined,但是赋值了默认值,所有age=20
嵌套结构对象的解构
let obj={arr:["hello",{msg:'world'}]
}// arr并不是一个变量,只是一个标志
let {arr:[str,{msg
}]}=objconsole.log(str,msg)// 再看一个案例let obj = {local: {start: {x: 20,y: 30}}};let {local: {start: {x,y}}} = obj;console.log(x, y);
字符串的解构赋值
let [a,b,c,d,e,f]='itcast'
console.log(a,b,c,d,e,f)// 解构length属性
let {length:len
}='itcast'console.log('len',len)
函数参数的解构
function test([x,y]){return x+y
}test([3,6])
72. 解构赋值的好处
交换变量的值
let num1=3
let num2=4
[num1,num2]=[num2,num1]
函数可以返回多个值
function test(){return [1,2,3]
}const [a,b,c]=test()console.log(a,b,c)
函数返回对象
function test(){return {num1:3,num2:4}
}let {num1:res1,num2:res2}=test()
73. 扩展运算符(将数组分离成一个个参数)
- …arr
数组合并
let arr1=[1,2,3]
let arr2=[4,5,6]let newArr=[].concat(arr1,arr2)// 使用扩展运算符
let newArr2=[...arr1,...arr2]
代替apply方法
// 调用Math.max方法是,需要传递的参数为一个列表
// 通过apply使传递的参数变成一个数组
let arr=[1,2,87,7]
Math.max.apply(null,arr)// 可以直接使用展开运算符
Math.max(...arr)
73- rest运算符(将剩余参数组合成数组)
function add(...values) {console.log(values); // 5}add(2, 3);
切割变量
let arr=[1,2,3,4,5,6]const [num1,...arr1]=arr// num1 1
// arr1 [2,3,4,5,6]
代替arguments
function test(...values){// 直接调用数组的方法values.sort()
}// 之前的arguments参数
function test(){Array.prototype.slice.call(arguments).sort()
}
74. 扩展运算符和剩余运算符的区分
扩展运算符
- 将数组分离成一个个变量
- 出现在实参和赋值操作的右边
剩余运算符
- 将一个个变量组合成一个数组
- 出现在形参和赋值操作的左边
75. 箭头函数
直接返回对象
let f=()=>({a:1,b:2
})
this指向问题
// 箭头函数没有自己的this指向// 那其中的this指向哪里呢?
// 1. 找到箭头函数定义的位置,当前作用域
// 2. 定义位置的上一层作用域的上下文(this) 即为this指向
- 一个案例
let person = {userName: 'wangwu',getUserName() {setTimeout(() => {console.log(this.userName);},1000)}}person.getUserName();
那么在我们这个案例中,
setTimeout
函数中使用了箭头函数,箭头函数中用了this,
而这时this
指的是外层代码块也就是person
,所以箭头函数中使用的this指的就是person
(包含箭头函数最近的函数是setTimeout
,那么包含setTimeout
这个函数的最近的函数或者是对象是谁呢?对了,是getUserName
这个函数,而getUserName
这个函数是属于哪个对象呢?是person
,所以this
为person
)let person = {userName: 'zhangsan',getUserName() {return () => {console.log(this.userName);}}}person.getUserName()(); // zhangsan
箭头函数的特性
不能使用call,apply,bind改变this指向
- 为什么:其实就是把这个箭头函数赋值给新的this指向,this中就有一个对象使这个箭头函数,而箭头函数中查找this又去当前对象的父亲哪里找,就没有达到修改this的目的
不能使用new操作符
- 还是this的问题
没有自己的
prototype属性
原型上的函数(prototype.methodxxx)不要写成箭头函数
- 还是this问题
76. ES6中属性和方法的简写
let userName = 'zhangsan';let userAge = 18;let person = {// userName:userName// userAge:userAgeuserName,userAge,// 原本// sayHello:function(){}sayHello() {console.log('Hello');}}person.sayHello();
77 浅拷贝
- Object.assign(target,src1,src2…)
属性同名后面覆盖前面
不可枚举的属性无法拷贝
let obj1={}
let obj2={a:1,b:2}Object.defineProperty(obj2,"c",{enumerable:false
})
78 Symbol
- 为了防止命名冲突的问题
let s=Symbol()
let s2=Symbol()console.log(s) // Symbol()
console.log(s2) // Symbol()
- 传入字符串来表示区别不同的Symbol
let s=Symbol('s')
let s2=Symbol('s2')console.log(s) // Symbol('s')
console.log(s2) // Symbol('s2')// 唯一标识
console.log(Symbol('s')===Symbol('s')) // false
- symbol作为属性名
let s=Symbol("s")let obj={// 2[s]:"hello"
}// 1
obj[s]="hello"//3
Object.defineProperty(obj,s,{// 属性...
})
- 防止属性名覆盖
let obj = {name: 'zs',age: 18}let mySymbol = Symbol('lib1');function test1(obj) {obj[mySymbol] = 42;}let mySymbol2 = Symbol('lib2');function test2(obj) {obj[mySymbol2] = 369;}test1(obj);test2(obj);console.log(obj);
79. Proxy
- 对象前面的拦截层(代理),在对莫格对象进行访问或者处理时,先经过这个拦截层,代理这次操作
Proxy语法
let proxy=new Proxy(target,handler)// target 要进行拦截的对象
// handler 拦截的处理对象
get拦截(拦截对get)
let student={userName:'张三'
}let proxy=new Proxy(student,{// target,目标对象// property 对象的属性get:function(target,property){// 对属性进行判断,看是否存在if(property in target){return target[property]}else{throw new Error("属性不存在")}}
})// 访问属性(注意是proxy,而不是student)
console.log(proxy.userName)
console.log(proxy.userAge)
set拦截
let student={name:'zs',age:18
}let proxy=new Proxy(student,{// target 目标对象// prop:属性名// value:属性值set:function(target,prop,value){// 对要设置的值进行合法性的校验if(prop==='age'){if(!Number.isInteger(value)){throw new TypeError('年龄不是整数')}if(value>60){throw new RangeError('年龄太大了')}}}
})// 访问属性(注意是proxy,而不是student)
proxy.age='80'
console.log(proxy.age)
应用场景1—值的校验
class Person{constructor(){this.name=''this.age=19// 返回的是一个代理对象return validator(this,personValidators)}
}// 校验规则器
const personValidators={name(val){return typeof val==='string'},age(val){return typeof val==='number'&&val>18}
}// 实现代理的处理函数handler
function validator(target,validator){// target 为实例对象// validator 校验器return new Proxy(target,{_validator:validator,// 拦截赋值操作// target 实例对象// key 属性// value 值set(target,key,value){// 判断修改的值是否在对象中if(target.hasOwnProperty(key)){let vFn=this._validator[key]// 进行校验if(vFn(value)){// 通过return Reflect.set(target,key,value)}else{}}}})
}
应用场景2-vue3响应式原理
<script>// 获取输入框和p标签domlet input=document.querySelector("#input")let p=document.querySelector("#p")// 定义一个响应式的对象,即proxy对象let obj={text:''}let proxyObj=new Proxy(obj,{set(target,key,value){// 判断属性是否存储if(target.hasOwnProperty(key)){// 存在,数据驱动视图input.value=valuep.innerHtml=value}else{throw new Error("属性不存在")}}})// input的键盘事件也触发数据驱动视图input.addEventListener("keyup",function(e){newObj.text=e.target.value})</script>
应用场景-实现私有属性
const obj={_id:555,// 提供对外的get和set方法getUserId(){return this._id},setUserId(val){this._id=val }
}// 使用proxy代理实现变量私有,不允许直接修改和获取let proxyObj=new Proxy(obj,{get(target,key){// 判断读取的属性是否在target,并且不是下划线开头if(target.hasOwnProperty(key)&&key[0]!=='_'){return target[key]}else{return undefined}},set(target,key,value){// 赋值时保证属性存在,且不是下划线开头if(target.hasOwnProperty(key)&&key[0]!=='_'){target[key]=value}}
})
80 set
长度:
new Set().size
转换为数组
Array.from(new Set())
数组到set
new Set(arr)
JavaScript-cnblog相关推荐
- Javascript匿名函数
ü 定义 匿名函数的定义非常简单:就是没有名字的函数.但是其用途非常的大 ü 典型的函数定义方式 在看匿名函数之前我们先看下在Javascript中定义一个函数比较典型的几种方式 函数声明 fun ...
- JavaScript异步编程(1)- ECMAScript 6的Promise对象
JavaScript的Callback机制深入人心.而ECMAScript的世界同样充斥的各种异步操作(异步IO.setTimeout等).异步和Callback的搭载很容易就衍生"回调金字 ...
- 前端面试宝典——来自cnblog
前言 本文总结了一些优质的前端面试题(多数源于网络),初学者阅后也要用心钻研其中的原理,重要知识需要系统学习,透彻学习,形成自己的知识链.万不可投机取巧,只求面试过关是错误的! 面试有几点需注意:(来 ...
- 来一些方便的小操作:博客园(cnblog)自定义界面
可以做点什么呢? 例如: 或者: 目录: 侧边栏分享功能 侧边工具栏 访问统计 RevolverMaps 背景更换 网页天气插件 在实现这些功能之前,需要具备 ...
- cnblog优化指南
为什么要优化 cnblog用了一段时间,发现好多问题 编辑器功能不足,影响编辑效率 美化不足,影响视觉体验 功能优化 自动添加目录 根据h2,h3标签自动生成目录,点击目录的指定条目可以跳转的指定类容 ...
- [na]整一下博客面貌--cnblog css定制
前言 之前觉得cnblog排版乱. 而csdn对word兼容性较好一些.所以就转到csdn了. 后来看到别人的cnblog排版挺好,如 等效果. 参考资料 http://www.cnblogs.com ...
- 【AJAX】JavaScript的面向对象
Ajax中后端数据返回后需要前端通过JavaScript来实现动态数据更新的问题.所以,在Ajax中加深了一遍JavaScript面向对象的印象. 基础部分: JavaScript中创建对象并简单对象 ...
- 【JavaScript总结】JavaScript语法基础:JS高级语法
作用域链: 1.JS中只有函数能够限定作用域的范围: 2.变量处理在制定的函数范围内,还有一个特殊的作用域,就是没有用var 声明的全局作用域 3.js中的作用域链是为了清晰的表示出所有变量的作用范围 ...
- 【JavaScript总结】JavaScript语法基础:DOM
->DOM的理解:文档对应dom树 ->有了DOM能做什么:DOM的操作 html文档做为DOM树模型,DOM树的节点就是对象.对象会触发事件来执行一些事件代码. C#中的事件是一个委托变 ...
- 【JavaScript总结】JavaScript语法基础:JS编码
运算符 数学:+. -. *. / 逻辑:>. < .>= .<=. == . !=.&&.|| . === .!==(完全等于) 对象相关 new delet ...
最新文章
- 又一家明星机器人公司倒掉:曾是全球机器人技术50强,主打性价比AI机械臂
- 行人识别学习资料整理2018
- 什么是网络推广浅析网站在优化时该如何让蜘蛛自觉爬行网站?
- 从一个被Tomcat拒绝的漏洞到特殊内存马
- SAP UI5和Angularjs事件处理机制的实现比较
- iOS15实现音乐播放器
- MySQL中order by中关于NULL值的排序问题
- qt5python gui cookbook_Python GUI Programming Cookbook学习笔记
- php在页面中实现累加,小白问个php累加问题
- 企业打开云HBase的正确方式,来自阿里云云数据库团队的解读
- effective_transformer
- python如何用xpath爬取指定内容_Python如何利用Xpath选择器爬取京东网商品信息
- kali mysql 卸载,linux mysql卸载命令
- Ubuntu Dolphinscheduler 执行命令 source: 未找到
- 别人总结归纳很全的三方库
- 使用Sinc卷积从原始音频数据进行轻量级的端到端语音识别
- nginx反向代理和正向代理
- 「群体遗传学实战」第三课: 如何对SNP位点进行过滤
- 2022.3.20 春分
- 半导体器件相关专业词汇积累ing