JavaScript-cnblog

JavaScript

文章目录

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=username
    this.sex=sex
    
    // 这里默认return this
}

// 等价于

// new 关键字做的事情
	var person={}
    person.__proto__=Person.prototype
    Person.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=name
    this.age=age
    
}


Person.prototype.sayHi=function(){
    console.log('666')
}

function New(){
    
    var obj={}
    
    Person.call(obj,arguments)
    
    obj.__proto__=Person.prototype
    
    return 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.length
    
    for(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=false
    
    for(let i=0;i<length;i++){
        let res=callback.call(this,this[i],i)
        
        if(res){
            // 满足条件--->加入数组
            flag=true
            break;
        }
    }
    
    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++){
        
        // 注意这里为新的initValue
        initValue=callback.call(this,initValue,this[i],i)
        
        
        
    }
    
    return initValue
    
}

29. 函数定义的三种方式

  • 函数声明
  • 函数表达式
  • 构造函数
// 构造函数的形式,最后一个参数为函数体
var fn=new Function('num1','num2','return num1+num2')

构造函数创建函数的特点

  • 效率较低
  • 作用域为顶级作用域
var a=12

function fn(){
    
    var a=11
    return 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=1
    function b(){
        a=10
        return
        function a(){}
    }
    
    b()
    console.log(a)
}
foo()

// 1

// 代码等价于
function foo(){
    var a;
    function b(){
        function a(){}
        a=10
        return
    }
    
    a=1
    b()
    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=10
      var obj = { num: 20 }; // obj.num=20
      obj.fn = (function (num) {
        this.num = num * 3; // 立即执行 window.num=60
        num++; // 立即执行 obj.num=21
        return function (n) {
          this.num += n; // fn(5) wind.num=65 // fn(10) obj.num=30
          num++;	// fn(5) obj.num=22 // fn(10) 23
          console.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.data
            console.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指向window  
          this.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=startPerson
    
    return 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, //添加constructor
        userName: "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=age
    
    this.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.prototype
      Super.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||document
        context=context||document
        if(selector.nodeType){
            // 是DOM元素
            this[0]=selector
            this.length=1
            this.context=selector
            return 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||document
        context=context||document
        if(selector.nodeType){
            // 是DOM元素
            this[0]=selector
            this.length=1
            this.context=selector
            return 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||document
        context=context||document
        if(selector.nodeType){
            // 是DOM元素
            this[0]=selector
            this.length=1
            this.context=selector
            return 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||document
        context=context||document
        if(selector.nodeType){
            // 是DOM元素
            this[0]=selector
            this.length=1
            this.context=selector
            return 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); //HTMLCollection
    console.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";
        //请求的url
        var 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.callback
    console.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}=obj

console.log(name,age)

// age本来应该是undefined,但是赋值了默认值,所有age=20
嵌套结构对象的解构
let obj={
    arr:["hello",{
        msg:'world'
    }]
}

// arr并不是一个变量,只是一个标志
let {arr:[str,{
    msg
}]}=obj

console.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:userAge
            userName,
            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标签dom
    let 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=value
                p.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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值