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. 比较运算符

三等于运算符

  1. 先比较值的类型是否相同
  2. 再比较值是否相同
// Number存在类型转换
1===Number(1)  true// new出来的是基本数据类型的包装类型
1===new Number(1) false//
'hello'===String('hello') // 都是基本类型true

双等于运算符

  • 比较更加复杂

    1. 先比较值的类型是否相同
    2. 不相同则进行隐式类型转换,转换为相同的类型,再进行值的比较

隐式转换规则

  • 字符串和数值—>字符串转换为数值
  • 一方为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来去请求正规网站,正规网站返回信息被恶意利用

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,所以thisperson)

  •     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())

  • 数组到setnew Set(arr)

JavaScript-cnblog相关推荐

  1. Javascript匿名函数

    ü  定义 匿名函数的定义非常简单:就是没有名字的函数.但是其用途非常的大 ü  典型的函数定义方式 在看匿名函数之前我们先看下在Javascript中定义一个函数比较典型的几种方式 函数声明 fun ...

  2. JavaScript异步编程(1)- ECMAScript 6的Promise对象

    JavaScript的Callback机制深入人心.而ECMAScript的世界同样充斥的各种异步操作(异步IO.setTimeout等).异步和Callback的搭载很容易就衍生"回调金字 ...

  3. 前端面试宝典——来自cnblog

    前言 本文总结了一些优质的前端面试题(多数源于网络),初学者阅后也要用心钻研其中的原理,重要知识需要系统学习,透彻学习,形成自己的知识链.万不可投机取巧,只求面试过关是错误的! 面试有几点需注意:(来 ...

  4. 来一些方便的小操作:博客园(cnblog)自定义界面

    可以做点什么呢? 例如: 或者:  目录: 侧边栏分享功能 侧边工具栏        访问统计 RevolverMaps      背景更换         网页天气插件 在实现这些功能之前,需要具备 ...

  5. cnblog优化指南

    为什么要优化 cnblog用了一段时间,发现好多问题 编辑器功能不足,影响编辑效率 美化不足,影响视觉体验 功能优化 自动添加目录 根据h2,h3标签自动生成目录,点击目录的指定条目可以跳转的指定类容 ...

  6. [na]整一下博客面貌--cnblog css定制

    前言 之前觉得cnblog排版乱. 而csdn对word兼容性较好一些.所以就转到csdn了. 后来看到别人的cnblog排版挺好,如 等效果. 参考资料 http://www.cnblogs.com ...

  7. 【AJAX】JavaScript的面向对象

    Ajax中后端数据返回后需要前端通过JavaScript来实现动态数据更新的问题.所以,在Ajax中加深了一遍JavaScript面向对象的印象. 基础部分: JavaScript中创建对象并简单对象 ...

  8. 【JavaScript总结】JavaScript语法基础:JS高级语法

    作用域链: 1.JS中只有函数能够限定作用域的范围: 2.变量处理在制定的函数范围内,还有一个特殊的作用域,就是没有用var 声明的全局作用域 3.js中的作用域链是为了清晰的表示出所有变量的作用范围 ...

  9. 【JavaScript总结】JavaScript语法基础:DOM

    ->DOM的理解:文档对应dom树 ->有了DOM能做什么:DOM的操作 html文档做为DOM树模型,DOM树的节点就是对象.对象会触发事件来执行一些事件代码. C#中的事件是一个委托变 ...

  10. 【JavaScript总结】JavaScript语法基础:JS编码

    运算符 数学:+. -. *. / 逻辑:>. < .>= .<=. == . !=.&&.|| . === .!==(完全等于) 对象相关 new delet ...

最新文章

  1. 又一家明星机器人公司倒掉:曾是全球机器人技术50强,主打性价比AI机械臂
  2. 行人识别学习资料整理2018
  3. 什么是网络推广浅析网站在优化时该如何让蜘蛛自觉爬行网站?
  4. 从一个被Tomcat拒绝的漏洞到特殊内存马
  5. SAP UI5和Angularjs事件处理机制的实现比较
  6. iOS15实现音乐播放器
  7. MySQL中order by中关于NULL值的排序问题
  8. qt5python gui cookbook_Python GUI Programming Cookbook学习笔记
  9. php在页面中实现累加,小白问个php累加问题
  10. 企业打开云HBase的正确方式,来自阿里云云数据库团队的解读
  11. effective_transformer
  12. python如何用xpath爬取指定内容_Python如何利用Xpath选择器爬取京东网商品信息
  13. kali mysql 卸载,linux mysql卸载命令
  14. Ubuntu Dolphinscheduler 执行命令 source: 未找到
  15. 别人总结归纳很全的三方库
  16. 使用Sinc卷积从原始音频数据进行轻量级的端到端语音识别
  17. nginx反向代理和正向代理
  18. 「群体遗传学实战」第三课: 如何对SNP位点进行过滤
  19. 2022.3.20 春分
  20. 半导体器件相关专业词汇积累ing

热门文章

  1. 学习理发去哪里_想学理发,去哪里学比较好
  2. presto 0.166概述
  3. 关于初学者Invalid byte tag in constant pool: 19错误
  4. Leangoo看板协作工具与Trello的差异
  5. 活动报名 | 生命科学中的生成式人工智能:如何搭建生命科学的“ChatGPT”
  6. 邓大人说,米兔要摸着石头过河
  7. 求知欲 心有所想,心有所选
  8. 一些不错的Java电子书下载
  9. [DeepSpeed]初代chatGPT模型部署实践
  10. 微信小程序之创建顶部tab菜单栏切换(新手学习)