JS基础1

247 阅读14分钟

类型转换的相关知识点记录

    let str1 = "houdunren"
    console.log(typeof str1); // string
    console.log(str1.substr(3)); // dunren
    let cms = new String('hdcm')
    console.log(typeof cms); // object
    console.log(cms.substr(3)); // m

直接通过""(引号)创建的字符串类型,值能和对象一样引用方法是因为系统(解析器)隐式的变成对象来使用

Boolean

声明方式

    // 语法书写
    const boolean = new Boolean(false)
    console.log(typeof boolean)
    console.log(boolean.value)
    if (boolean.valueOf()) {
        console.log("goodgoodstudy")
    }
    // 字面量写法
    let hd = false
    console.log(typeof hd)
    if (hd) {
        console.log("daydayup")
    }

boolean隐式转换原理

boolean ==》数值

不同类型进行比较,转换同一个类型(数值)

    let number = 0;
    console.log(number == false) // 0 == 0
    let number = 89;
    console.log(number == true) // 98 == 1 ? ===> false

非0的数值做表达式运算的时候为TRUE

    if (number) {
      console.log('true');
    }
    // ====>
    console.log(Boolean(number))

boolean ==》字符串

    let hd = '0'
    console.log(Number(hd)) // 0
    console.log(hd == false) // 0 == 0 ==> true
    let hd1 = '1'
    console.log(Number(hd1)) // 1
    console.log(hd == true) // 1 == 1 ==> true

字符串(非空)做表达式运算的时候为TRUE

    let hd = '0'
    if (hd) {
      console.log('true');
    }
    // ====>
    console.log(Boolean(hd))
    let hd = ''
    if (!hd) {
      console.log('true');
    }
    // ====>
    console.log(Boolean(hd)) // false

boolean ==》数组

    let arr = []
    console.log(Number(arr)) // 0
    console.log(arr == false) // 0 == 0 ==> true
    let arr1 = [1]
    console.log(Number(arr1)) // 1
    console.log(arr1 == true) // 1 == 1 ==> true
    let arr2 = [1, 2]
    console.log(Number(arr2)) // NaN
    console.log(arr2 == true) // NaN == 1 ==> false

数组做表达式运算的时候为TRUE

    let arr = []
    if (arr) {
      console.log('true');
    }
    // ====>
    console.log(Boolean(arr)) // true

数组是应用类型,一个应用类型转换成Boolean类型都会转换成true,比较是转换为number

显示转换Boolean类型

不是解析器自动完成,人为地控制

    let number = 0
    console.log(typeof number); // number
    number = !!number
    // !把Boolean取反,number==》Boolean,Boolean取反
    // 一个!把Boolean取反,number==》Boolean,Boolean取反,另一个!在取反之后再取反
    console.log(number);//false
    let str = 'study'
    console.log(!!str) // true
    let arr = []
    console.log(!!arr) // true
    let date = new Date()
    console.log(!!date) // true

Boolean()

    let number = 0
    console.log(Boolean(number)) // false
    let str = 'study'
    console.log(Boolean(str)) // true
    let arr = []
    console.log(Boolean(arr)) // true
    let date = new Date()
    console.log(Boolean(date)) // true

Boolean实例操作

    while(true) {
      const year = prompt('请输入现在的年份').trim()
      if (!year) continue
      let date = new Date()
      let nowyear = date.getFullYear()
      console.log(year == nowyear ? '回答正确' : '输入错误');
      break
    }

Number

声明方式与基本函数

    let num = new Number(99)
    console.log(num.valueOf()); // 99
    console.log(typeof num);// object
    let number = 99
    console.log(number);
    console.log(typeof number); // number
    console.log(number.valueOf()); // 99
    let num1 = number.valueOf() // valueOf()取得数值的值
    console.log('num1', num1); // 99
    console.log(typeof num1); // number
    let str = number.toString()
    console.log('str', str);
    console.log(typeof str); // string

数值类型的静态方法

Number.isInteger(number)

    let number1 = 99
    let res1 = Number.isInteger(number1)
    console.log(res1)

number.toFixed(保留几位小数)

    let number2 = 99.023213
    let res2 = number2.toFixed(2)
    console.log(res2);

数值类型转换技巧和NaN类型

image.png

        // NaN:不是一个数字
        console.log(Number('study')); // NaN
        console.log(2 / 'ssss'); // NaN
        console.log(NaN == NaN); // false
        console.log('nan', Number('NaN'));
        console.log(Number.isNaN(2 / 'ssss')); // true
        console.log(Object.is(2 / 'qqq', NaN)); // true
        console.log(Number(false)); // 0
        console.log(Number(true)); // 1
        // 纯数字的字符串
        let res = '12'
        console.log(Number(res));
        const num = document.querySelector("[name='number']").value
        console.log(num);
        console.log(typeof num);
        // 前面是数值 + 其他类型的字符
        let res2 = '12sssss'
        console.log(Number(res2)); // NaN
        console.log(parseInt(res2)); // 12
        // 浮点数
        let res3 = '12.12sssss'
        console.log(Number(res3)); // NaN
        console.log(parseInt(res3)); // 12
        console.log(parseFloat(res3)); // 12.12
        // 空对象
        console.log('{}', Number({})); // NaN
        console.log(Number({
            valueOf: function() {
                return 99
            }
        })); // 99

Math数学计算

        console.log(Math.max(false, true)); // 1 ====> fasle = 0, true = 1
        console.log(Math.max('12ppp', 13)); // NaN
        // 获取数组中最大值
        // Math.max不支持数组
        let grade = [20, 88, 60, 99]
        console.log(Math.max(grade)); // NaN
        console.log(Math.max.apply(null, grade)); // 指定数组的形式传参 99
        console.log(Math.ceil(5.1)); // 6
        console.log(Math.ceil(5.9)); // 6
        console.log(Math.floor(5.0001)); // 5
        console.log(Math.floor(5.9)); // 5

求0-x之间的随机数

Math.floor(Math.random() * (x + 1))

        // 获取0-5的随机数
        // Math.random >= 0 && < 1
        console.log(Math.floor(Math.random() * (5 + 1)));
    // index: 0 ~ (student.length - 1)
    // (x + 1) ===> (student.length - 1) + 1 ===> student.length
    const student = ['张三', '李四', '王五', '赵六', '田七']
    const index = Math.floor(Math.random() * student.length)
    console.log(student[index]);

求x-y之间的随机数

x + Math.floor(Math.random() * (y - x + 1))

    // 随机打印index: 2 ~ (student.length - 1)
    // (y - x + 1) ===> (student.length - 1) - 2 + 1 ===> student.length
    const student = ['张三', '李四', '王五', '赵六', '田七']
    const index = 2 + Math.floor(Math.random() * (student.length - 2))
    console.log(student[index]);
      function arrayRandomValue(array, start = 0,end){
          // end是元素的个数,不是 index
          end = end ? end : array.length;
          const index = start + Math.floor(Math.random() * (end - start))
          return array[index]
      }

Date

时间戳

    const date = new Date()
    console.log(date);
    console.log(typeof date);
    console.log(date * 1); // 返回时间戳
    const timedate = Date.now()

计算脚本执行时间

    console.time('for') // 'for': 标志位
    for(var i = 0; i < 100;i++) {}
    console.timeEnd('for')

根据具体的时间获取日期

    const date = new Date('1990-9-22 3:22:18')
    console.log(date) // Sat Sep 22 1990 03:22:18 GMT+0800 (中国标准时间)
    console.log(date.getMonth()) // 8
    const date1 = new Date(1990,2,22,13,22,19)
    console.log(date1); // Thu Mar 22 1990 13:22:19 GMT+0800 (中国标准时间)
    const param = [1990, 2, 22, 13, 22, 19]
    const date2 = new Date(...param)
    console.log(date2); // Thu Mar 22 1990 13:22:19 GMT+0800 (中国标准时间)

ISO(标准时间)和TIMESTAMP(时间戳)格式互换

时间戳

    const date = new Date()
    console.log(date * 1);
    console.log(Number(date));
    console.log(date.valueOf());
    console.log(date.getTime());

时间戳转标准时间

    const date = new Date()
    const timestamp = date.valueOf()
    console.log(new Date(timestamp));

封装日期格式化函数

    const date = new Date('1992-2-12 10:22:12')
    function dateFormat(date, format = 'YYYY-MM-DD HH:mm:ss') {
      const config = {
        YYYY: date.getFullYear(),
        MM: date.getMonth(),
        DD: date.getDate(),
        HH: date.getHours(),
        mm: date.getMinutes(),
        ss: date.getSeconds(),
      }
      for (const key in config) {
        format = format.replace(key, config[key])
      }
      return format
    }
    console.log(dateFormat(date, 'YYYY年MM月DD日')); // 1992年1月12日

优秀日期处理库

momentjs

momentjs

  <script src="http://cdn.staticfile.org/moment.js/2.24.0/moment.min.js"></script>
  <script>
    const date = moment('1992-2-22 10:11:11')
    console.log(date.format('YYYY-MM-DD HH:mm:ss'));
  </script>

image.png

出现的警告:MM处建议两位 1992-02-22

  <script src="http://cdn.staticfile.org/moment.js/2.24.0/moment.min.js"></script>
  <script>
    const date = moment()
    console.log(date.format('YYYY-MM-DD HH:mm:ss')); // 2021-12-27 23:28:12
    console.log(date.add(10, 'days').format('YYYY-MM-DD HH:mm:ss')); // 2022-01-06 23:28:12
  </script>

数组

    // 构造函数
    const arr = new Array('ss', 'gg', 'is')
    console.log(arr);
    // 字面量
    const arr1 = [1, 2, 3]
    console.log(arr1);

数组是引用型数据

    const arr = [1, 2]
    arr[1] = 99
    // 这里const定义的可以修改的原因是,数组是引用型数据,根据下标去修改内容,地址内容不变

console.table

image.png

多维数组操作

    let arr = [['a'], ['b']

数组创建细节

    let ds = ['dsjy']
    ds[3] = 'sd'
    console.log(ds);

image.png

    let ds = new Array(1, 2, 3, 4)
    console.log(ds.length); // 4
    console.log(ds) // [1, 2, 3, 4]
    let ds1 = new Array(6)
    console.log(ds1.length); // 6
    console.log(ds1) // [empty*6]

类型检测和转换

  • 检测

Array.isArray([])

  • 转换
    // 数组转字符串
    let ds = [1, 2, 3].toString()
    let ds1 = String([1, 2, 3])
    let ds2 = [1, 2, 3].join(",")
    console.log(typeof ds); // string
    console.log(ds, ds1, ds2); // 1,2,3 1,2,3 1,2,4
    let str = 'dshm'
    console.log(str.split("")); // ['d', 's', 'h', 'm']
    console.log(Array.from(str)); // ['d', 's', 'h', 'm']
    // 元素有length属性都可以转为数组
    let str1 = 'dshm,ggs'
    console.log(str1.split(",")); // ['dshm', 'ggs']

元素有length属性都可以转为数组

    let obj = {
        name: 'ds',
        age: 11
    }
    console.log(Array.from(obj)); // []
    let obj = {
        name: 'ds',
        age: 11,
        lenght: 2
    }
    console.log(Array.from(obj)); // [undefined, undefined]

===>

    let obj = {
       0: 'ds',
       1: 11,
       length: 2
    }
    console.log(Array.from(obj)); // ['ds', 11]

Array.from

Array.from(数组, function(item) { // item为数组的每一项 })

    let divs = document.querySelectorAll('div')
    let arr = Array.from(divs, function(item) {
       item.style.backgroundColor = 'red'
       return item
    })
    console.log('arr', arr);

Array..of

展开语法(...)

  • 数组中使用
        let arr = [1, 2]
        let brr = [3, 4]
        arr = [...arr, ...brr] // [1, 2, 3, 4]
  • 函数参数中使用
        function sum(...args) {
            return args.reduce((p, n) => {
                return (p += n)
            }, 0)
        }
        console.log(sum(1,2,3));
  • DOM节点中使用
    const div = document.querySelectorAll('div');
    // dom获取的数组是伪数组,无法直接使用数组上的一些方法
    // 伪数组 ==》 数组
    Array.from(div, function(item){
       console.log(item);
    });
    Array.prototype.map.call(div, function(item){
       console.log(item);
    });
    [...div].map(function(item){
       item.addEventListener('click', function(){
          item.style.color = 'blue'
       })
    })

解构赋值

        let arr = ['s', 18]
        let [name, age] = arr
        console.log(name, age); // s 18

        function get() {
            return ['d', 16]
        }
        let [name1, age1] = get()
        console.log(name1, age1); // d 16

解构赋值的时候,需要用let、var、const定义,不然在“use strict”(严格模式下)会报错

结构和展开运算符的整合运用

    let [name, ...args] = ['好好学习', '天天向上', 2021]
    console.log()
    console.log(args); // ['天天向上', 2021]
    let a = ['ss', ...args]
    console.log(a) // ['ss', '天天向上', 2021]
  • ... 放在变量的位置(只能放在最后),就是收集

  • ... 放在值的位置,就是展开

    let [name, year='天天向上'] = ['好好学习']
    console.log(year) // '天天向上'

添加元素的多种操作方法

数组最后一位添加

  • arr[arr.length]
  • [...arr, ...brr]
  • arr.push()
        let arr = ['好好', '学习']
        arr[arr.length] = '天天'
        let ds = ['shop', 'sss']
        arr = [...arr, ...ds]
        arr.push('向上')

数据出栈与入栈及填充操作

  • pop
  • push
  • unshift
  • shift
  • fill:arr.fill['需要填充的内容',从哪个位置开始,到哪个位置结束]
    let arr = [1, 2, 3, 6]
    let deleteData = arr.pop() // 被删除的数据
    console.log('pop', deleteData);
    let res1 = arr.push(5) // 数组的长度
    console.log('push', res1);
    console.log(Array(5).fill('学习')); // ['学习', '学习', '学习', '学习', '学习']
    let res2 = ['好好', '天天']
    res2.fill('学习', 1)
    console.log(res2); // ['好好', '学习']

数组的增删改查

splice

array.splice(start[, deleteCount[, item1[, item2[, ...]]]] )

  • start:
  • deleteCount
  • item1, item2,...

slice

arr.slice([begin[, end])

  • begin:从第几位开始截取原数组的元素,
    • 省略begin,索引从0开始截取
    • 超出原数组的索引范围,返回空数组
  • end: 截取到第几位(包含 begin,但不包含 end
    • 省略end,begin开始截取到最后一位
    • end > arr.length, begin开始截取到最后一位
    • 为负数,在原数组中的倒数第几个元素结束抽取 slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)
  • 不改变原数组长度
  • 返回值为截取的内容
        let sliceArr1 = arr.slice()
        console.log(sliceArr1); // [1, 2, 3, 4, 5]
        let sliceArr2 = arr.slice(0, 1) 
        console.log(sliceArr2); // [1]
        let sliceArr3 = arr.slice(1)
        console.log(sliceArr3); // [2, 3, 4, 5]
        let sliceArr4 = arr.slice(1, 99)
        console.log(sliceArr4); // [2, 3, 4, 5]
        let sliceArr5 = arr.slice(-2, -1)
        console.log(sliceArr5); // [4]
        let sliceArr6 = arr.slice(1, -1)
        console.log(sliceArr6); // [2, 3, 4]

find

arr.find(callback[, thisArg])

  • callback: 在数组每一项上执行的函数,接收 3 个参数
    • element: 当前遍历到的元素
    • index(可选): 当前遍历到的索引
    • array(可选):数组本身
  • thisArg(可选):执行回调函数时用作this的对象

返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined

        var inventory = [
            {name: 'apples', quantity: 2},
            {name: 'bananas', quantity: 0},
            {name: 'cherries', quantity: 5}
        ];
        var obj = inventory.find(el => {
            console.log('find', el);
            return el.name === 'apples'
        })
        console.log('obj', obj); // {name: 'apples', quantity: 2}

自己写find

    function find(array, callback){
      for (const value of array) {
        if (callback(value)) return value;
      }
      return undefined
    }

    let arr = [1,2,3,4]
    let res2 = find(arr, item => {
      return item == 3
    })
    console.log('res2', res2); // 3
    Array.prototype.findValue = function (callback){
      // this为调用这个函数的对象
      for (const value of this) {
        if (callback(value)) return value;
      }
      return undefined
    }
    let res3 = arr.findValue(function(item){
      return item === 2
    })
    console.log('res3', res3); // 2

findIndex

arr.findIndex(callback[, thisArg])

  • callback: 在数组每一项上执行的函数,接收 3 个参数
    • element: 当前遍历到的元素
    • index(可选): 当前遍历到的索引
    • array(可选):数组本身
  • thisArg(可选):执行回调函数时用作this的对象数组中通过提供测试函数的第一个元素的索引。否则,返回-1

数组中通过提供测试函数的第一个元素索引。否则,返回-1

    var inventory = [
      { name: 'apples', quantity: 2 },
      { name: 'bananas', quantity: 0 },
      { name: 'cherries', quantity: 5 }
    ];
    var res = inventory.findIndex(el => {
      console.log('findIndex', el);
      return el.name === 'bananas'
    })
    console.log('res', res); // 1

数组排序

sort

        let cart = [
            {name: 'iphone', price: 12000},
            {name: 'imac', price: 18000},
            {name: 'ipad', price: 3000},
        ]
        cart.sort(function(a, b){
            return a.price - b.price
        })
        console.log(cart);

sort原理

    let arr = [1, 5, 3, 9, 7]
    function sort(array, cb){
      for (const n in array) {
        for (const m in array) {
          if(cb(array[n], array[m]) < 0){
            const temp = array[n]
            array[n] = array[m]
            array[m] = temp
          }
        }
      }
      return array
    }
    arr = sort(arr, function(a,b){
      return b - a
    })
    console.log(arr); // [9, 7, 5, 3, 1]

循环操作中引用类型使用技巧

for of

for...of

官网说明:for...of语句可迭代对象(包括 ArrayMapSetStringTypedArrayarguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句

    var arr = [
      {name:'苹果', price: 12},
      {name:'栗子', price: 10},
      {name:'板栗', price: 12}
    ]
    var brr = [1, 2, 3]
    for (let i = 0; i < arr.length;i++){
      brr[i] = i + 2
    }
    console.log(brr); // [2, 3, 4]
    // for of的复杂数据的内容可以更改
    for (const value of arr) {
      value.name = `当季${value.name}`
    }
    console.log('arr', arr);
    // for of 基本数据类型无法更改
    for (let value of brr) {
      value = `数字${value}`
    }
    console.log('brr', brr); // [2, 3, 4]
    var arr = [
      {name:'苹果', price: 12},
      {name:'栗子', price: 10},
      {name:'板栗', price: 12}
    ]
    var brr = [1, 2, 3]
    var crr = {
      name: 'sss',
      age: 12
    }
    for (const key in arr) {
      console.log('key--arr', key); // 索引
    }
    for (const key in brr) {
      console.log('key-brr', key); // 索引
    }
    for (const key in crr) {
      console.log('key-crr', key); // 键值
    }

forEach

    var arr = [1, 3, 4]
    var brr = [
      {
        num: 1
      },
      {
        num: 3
      },
      {
        num: 4
      }
    ]
    arr.forEach(item => {
      item = '1' + item
    })
    console.log(arr);
    brr.forEach(item => {
      item.num = '1' + item.num
    })
    console.log(brr);

image.png

循环的数据只可以修改引用型数据的内容

iterator迭代器方法玩转数组

    let arr = [1,2,3]
    let keys = arr.keys()
    console.log(keys); // Array Iterator {}

image.png

数组.keys()

    let arr = ['study', 'everyday']
    let keys = arr.keys()
    console.log(keys); // Array Iterator {}
    console.log(keys.next());
    /**
     * { value: 0, done: false}
     * value: 索引
     * done: 是否迭代完成
    */
    console.log(keys.next()); 
    /**
     * { value: 1, done: false}
     * value: 索引
     * done: 是否迭代完成
    */
    console.log(keys.next()); 
     /**
     * { value: undefined, done: true}
     * value: 索引
     * done: 是否迭代完成
    */

image.png

next()取值

数组.values()

    let arr = ['study', 'everyday']
    let values = arr.values()
    console.log(values); // Array Iterator {}
    console.log(values.next());
    /**
     * { value: study, done: false}
     * value: 值
     * done: 是否迭代完成
    */
    console.log(values.next()); 
    /**
     * { value: 1, done: false}
     * value: 值
     * done: 是否迭代完成
    */
    console.log(values.next()); 
     /**
     * { value: undefined, done: true}
     * value: 值
     * done: 是否迭代完成
    */

image.png

    let arr = ['study', 'everyday']
    let keys = arr.values()
    // 方法一
    while (({ value, done} = value.next()) && done === false) {
      console.log(value);
    }
    // 方法二
    for (const value of values) {
      console.log(value);
    }

image.png

数组.entries()

    let arr = ['study', 'everyday']
    let entries = arr.entries()
    console.log(entries.next());

image.png

    let arr = ['study', 'everyday']
    let entries = arr.entries()
    console.log(entries.next());
    // let { done, value } = entries.next()
    // console.log(done, value);
    // 解构
    let [a, b] = arr
    console.log('a: ', a, 'b: ', b);
    // 解构数据
    let { done, value: [index, val] } = entries.next()
    console.log(done, index, val);

image.png

    let arr = ['study', 'everyday']
    let entries = arr.entries()
    for (const [index, value] of entries) {
      console.log(index, value);
    }

image.png

高效处理数组的方法

some

some()  方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。 注意: 如果用一个空数组进行测试,在任何情况下它返回的都是false

    let arr = ['ss', 'ww']
    let res = arr.some(function(value, index, arr) {
      console.log(value);
      return false
    })
    console.log(res);

image.png

<body>
  <input type="text" name="title">
  <span></span>
  <script>
    let arr = ['php', 'js']
    let inputBox = document.querySelector('[name="title"]')
    inputBox.addEventListener("keyup", function(){
      const res = arr.some(key => {
        return this.value.indexOf(key) !== -1
      })
      document.querySelector('span').innerHTML = res ? '' :  document.querySelector('span').innerHTML = `必须包含${arr.join(',')}关键词`
    })
  </script>
</body>

every

every()  方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

注意:若收到一个空数组,此方法在一切情况下都会返回 true

    let arr = ['sss', 'nice']
    let status = arr.every(function(value, index, arr) {
      console.log(index);
      return true
    })
    console.log('status', status); // true

image.png

为真,循环继续,直至为false或者循环所有元素

    let arr = ['sss', 'nice']
    let status = arr.every(function(value, index, arr) {
      console.log(index);
      return false
    })
    console.log('status', status); // false

image.png

循环为false,就停止循环

    let user = [
      {name:'张三', score: 89},
      {name:'李四', score: 99},
      {name:'王五', score: 55},
    ]
    const res = user.every(function(item){
      return item.score >= 60
    })
    res ?  console.log('全部同学都及格') : console.log('有同学没有及格');

过滤filter

filter()  方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。

    let arr = [
      {name:'张三', score: 33},
      {name:'李四', score: 88},
      {name:'王五', score: 60}
    ]
    const res = arr.filter(function(value, index, arr){
      console.log(value);
      return value.score >= 60
    })
    console.log(res);

image.png

自定义过滤函数理解原理

    let arr = [1, 2, 3, 4]
    function filter(array, except) {
      let newArr = []
      for (const value of arr) {
        if(except.includes(value) === false) {
          newArr.push(value)
        }
      }
      return newArr
    }
    console.log(filter(arr, [2, 3])); // [1, 4]
    let arr = [1, 2, 3, 4]
    function filter(array, cb) {
      let newArr = []
      for (const value of arr) {
        if(cb(value) === true) {
          newArr.push(value)
        }
      }
      return newArr
    }
    console.log(filter(arr, function(value){
      return value > 2
    })); // [3, 4]

map映射数组和应用类型处理技巧

    let arr = [12, 66, 99]
    let newArr = arr.map(function(value, index, arr) {
      value = `学生数据-${value}`
      return value
    })
    console.log('arr', arr);
    console.log('newArr', newArr);

image.png

    let arr = [
      {name:'张三', score: 33},
      {name:'李四', score: 88},
      {name:'王五', score: 60}
    ]
    arr.map(function(value, index, arr) {
      value.isPassed = true // 改变原数组
    })

image.png

    let arr = [
      {name:'张三', score: 33},
      {name:'李四', score: 88},
      {name:'王五', score: 60}
    ]
    let newArr = arr.map(function(value, index, arr) {
      // 不影响原数组
      // return Object.assign({isPassed: true}, value)
      return {
          name: value.name,
          score: value.score,
          isPassed: true 
        }
    })
    console.log('arr', arr);
    console.log('newArr', newArr);

image.png

Object.assign()

Object.assign()

Object.assign(target, ...sources)

参数

  • target: 目标对象。
  • sources: 源对象。 返回值: 目标对象
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }

reduce

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

  • callback

  • 执行数组中每个值 (如果没有提供 initialValue则第一个值除外)的函数,包含四个参数:

  • accumulator

    • 累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。

    • currentValue

      数组中正在处理的元素。

    • index 可选

      数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。

    • array可选

      调用reduce()的数组

  • initialValue可选

  • 作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错

    let arr = [1,2,3,4,5]
    arr.reduce(function(pre, value, index, arr){
      console.log(pre, value);
    })

image.png

    let arr = [1,2,3,4,5]
    arr.reduce(function(pre, value, index, arr){
      console.log(pre, value);
      return 99
    })

image.png

    let arr = [1,2,3,4,5]
    arr.reduce(function(pre, value, index, arr){
      console.log(pre, value);
      return 99
    })
    arr.reduce(function(pre, value, index, arr){
      console.warn(pre, value);
      return 99
    }, 0)

image.png 重复出现的次数

    let arr = [1,2,3,1,1]
    function arrayCount(array, item){
      return arr.reduce(function(total,value){
        total += item == value ? 1 : 0
        return total
      },0)
    }
    console.log(arrayCount(arr, 1)); // 3

最大值

    let arr = [1,9,3,5,4]
    function arrayMax(array) {
      return arr.reduce(function(pre, cur){
        return pre > cur ? pre : cur
      })
    }
    console.log(arrayMax(arr));
    // 获取价格超过1万元商品的名称
    let cart = [
      {name: 'iphone', price: 12000},
      {name: 'imac', price: 25000},
      {name: 'ipad', price: 3600}
    ]
    function getNames (arr, price){
      return arr.reduce(function(pre, cur){
        cur.price > price ? pre.push(cur.name) : pre
        return pre
      }, [])
    }
    console.log(getNames(cart, 10000));

数组去重

    let arr = [1, 2,1,3,4,5,4,3]
    let newArr = arr.reduce(function(array, cur) {
      if (!array.includes(cur)){
        array.push(cur)
      }
      return array
    }, [])
    console.log(newArr); // [1,2,3,4,5]
    let arr = [
      {name: 'iphone', price: 12000},
      {name: 'imac', price: 25000},
      {name: 'ipad', price: 3600},
      {name: 'iphone', price: 12000},
      {name: 'imac', price: 25000},
      {name: 'ipad', price: 3600}
    ]
    function filterGood(goods){
      return goods.reduce(function(array, cur) {
        let find = array.find(function(v){
          return v.name === cur.name
        })
        if(!find) array.push(cur)
        return array
      }, [])
    }
    console.log(filterGood(arr));

image.png

Symbol

声明Symbol

Symbol(描述)

不是全局创建

    let study = Symbol('学习') // '学习'是描述
    let hard = Symbol('努力')
    console.log(study == hard); // false
    console.log(study.description);

description

获取描述

Symbol.for(描述)

全局保存,系统会记录

    let day = Symbol.for('date') // 系统会记录
    let tian = Symbol.for('date')
    let ri = Symbol.for('date1')
    console.log(day == tian); // true
    console.log(day == ri); // false
    console.log(Symbol.keyFor(day)); // date
    let hard = Symbol('努力')
    console.log(Symbol.keyFor(hard)) // undefined

keyFor

使用Symbol.for定义的获取描述。普通定义无法获取

symbol解决字符串耦合问题

image.png

image.png

symbol在缓存容器中的使用

    class Cache {
            static data = {}
            static set(name, value) {
                return (this.data[name] = value)
            }
            static get(name) {
                console.log('name', this.data);
                return this.data[name]
            }
        }
        Cache.set('sss', '123')
        let user = {
            name: 'apple',
            description: '用户资料'
        }
        let cart = {
            name: 'apple',
            description: '购物车'
        }
        // Cache.set('apple', user)
        // Cache.set('apple', cart)
        // 上面直接写会覆盖
        Cache.set('user-apple', user)
        Cache.set('cart-apple', cart)
        console.log(Cache.get('apple'));
    class Cache {
            static data = {}
            static set(name, value) {
                return (this.data[name] = value)
            }
            static get(name) {
                console.log('name', this.data);
                return this.data[name]
            }
        }
        Cache.set('sss', '123')
        let user = {
            name: 'apple',
            description: '用户资料',
            key: Symbol('资料')
        }
        let cart = {
            name: 'apple',
            description: '购物车',
            key: Symbol('购物车')
        }
        // Cache.set('apple', user)
        // Cache.set('apple', cart)
        // 上面直接写会覆盖
        Cache.set(user.key, user)
        Cache.set(cart.key, cart)
        console.log(Cache.get(user.key));

扩展属性与对象属性保护

    let symbol = Symbol('这是一个Symbol类型')
    let hd = {
        name: '素素',
        [symbol]: 'susu'
    }

Object.keys

    // for...in,for...of无法遍历到symbol
    for(const key in hd) {
      console.log('in', key); // name
    }
    for(const key of Object.keys(hd)) {
      console.log('of', key); // name
    }

Object.getOwnPropertySymbols

   // 遍历对象中的symbol属性
   for (const key of Object.getOwnPropertySymbols(hd)) {
       console.log('Object.getOwnPropertySymbols', key); // Symbol(这是一个Symbol类型)
   }

Reflect.ownKeys

    // 遍历所有的属性
    for (const key of Reflect.ownKeys(hd)) {
       console.log('Reflect.ownKeys', key); // name Symbol(这是一个Symbol类型)
     }
        let site = Symbol('这是一个Symbol')
        class User {
            constructor(name) {
                this.name = name
                this[site] = 'study.com'
            }
            getName(){
                return `${this[site]} ${this.name}`
            }
        }
        let user = new User('lisi')
        console.log(user.getName());
        for (const key in user) {
            console.log(key); // name
        }

Set

Set中的元素只会出现一次,即 Set 中的元素是唯一的。 Set-MDN

Set.add(value)

在Set对象尾部添加一个元素。返回该Set对象

let set = new Set()
set.add(1)
set.add(1)
set.add('1')
console.log(set);

image.png

不能有重复的值,但是可以添加不同类型的值

let set = new Set([1,1,2,3,4])
console.log(set); // Set(4) {1, 2, 3, 4}
        let obj = {
            1: 'susu',
            "1": 'SS'
        }
        console.log(obj); // {"1": 'SS'}
        let ds = {
            [obj]: '东酥'
        }
        // 对象做KEY,系统会将其转换为字符串
        console.log(ds.toString()); // [object Object]
        console.log(ds['[object Object]']); // 东酥
        console.log(ds[obj.toString()]); // 东酥

Set.clear()

移除Set对象内的所有元素。

Set.delete(value)

移除Set中与这个值相等的元素,返回Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在,返回true,否则返回false)。Set.prototype.has(value)在此后会返回false

Set.values()

返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。

        let set = new Set('dongsu', 'well')
        let set1 = new Set(['dongsu', 'well'])
        let set2 = new Set()
        set2.add(1)
        set2.add(2)
        console.log(set); // Set(6) {'d', 'o', 'n', 'g', 's', 'u'}
        console.log(set1); // Set(2) {'dongsu', 'well'}
        console.log(set2); // Set(2) {1, 2}
        console.log(set2 instanceof Object); // true
        console.log('clear', set.clear()) // undefined
        console.log('delete', set1.delete('well')); // true
        console.log(set); // Set(1) {size: 0}
        console.log(set1); // Set(1) {'dongsu'}
        console.log(set1.values()); // SetIterator {'dongsu'}

Set转数组

Array.from

    let set = new Set(['dongsu', '东酥'])
    let arr = Array.from(set)
    console.log(arr); //  ['dongsu', '东酥']

...

    let brr = [...set]
    console.log(brr); //  ['dongsu', '东酥']

数组去重

    let crr = [1,2,1,2,3,4,5]
    crr = [...new Set(crr)]
    console.log(crr); // [1, 2, 3, 4, 5]

遍历Set类型的方式

let set = new Set(['dongsu', '东酥'])
console.log(set.values()); // 获取所有元素
console.log(set.keys()); // key === value
console.log(set.entries()); // key === value, {key: value}
  • forEach
  • for...of

用set的小应用

    let obj = {
            data: new Set(),
            set keyword(word) {
                return this.data.add(word)
            },
            // keyword1(word) {
            //     return this.data.add(word)
            // },
            show(){
                let ul = document.querySelector('ul')
                ul.innerHTML = "";
                this.data.forEach(function(value){
                    ul.innerHTML += `<li>${value}</li>`
                })
            }
        }
        let input = document.querySelector("[name='dongsu']")
        console.log(input);
        input.addEventListener('blur', function(){
            obj.keyword = this.value
            // obj.keyword1(this.value)
            obj.show()
        })

并集、交集、差集算法实现

    let a = new Set([1,2,3,4,5])
    let b = new Set([4,5,6,7])
    // 并集
    console.log(new Set([...a, ...b]));
    // 差集
    console.log(
       new Set([...a].filter(function(item){
          return !b.has(item)
       }))
    );
    // 交集
    console.log(
       new Set([...a].filter(function(item){
           return b.has(item)
       }))
    );

WeakSet

与Set类似,但是必须是引用类型

    let set = new WeakSet('dongsu') // 不是引用类型,报错
    let set = new WeakSet(['dongsu']) // 本质是一个字符串,不是引用类型,报错
    let set = new WeakSet()
    set.add(['dongsu', 'good]) // 添加的类型是数组(引用类型)
  • add
  • delete
  • has
  <div>123</div>
  <div>456</div>
  <script>
    let nodes = new WeakSet()
    let divs = document.querySelectorAll('div')
    divs.forEach(item => {
      nodes.add(item)
    })
    nodes.delete(divs[0])
    console.log(nodes.has(divs[0])); // false
    console.log(nodes);
  </script>

不会有重复的值

WeakSet的弱引用特性

let hd = { name:'后盾人' }
    let edu = ds

image.png

WeakSet不会让引用计数器+1,这种特性为弱引用

    let hd = { name:'dongsu' }
    let edu = hd
    let set = new WeakSet()
    set.add(hd)
    hd = null
    edu = null
    console.log(set);

image.png

弱引用的影响:循环的时候,上面有符号,但是本质没有值。但是系统将WeakSet所有的循环方法不能使用

WeakSet保存对象数据,由于是弱引用类型迭代方法用不了

引用数据类型的垃圾回收原理

    // 基本数据类型
    let a = 1
    let b = a
    a = null
    console.log(b); // 1
    // 引用数据类型
    let obj = { name: 'dongsu' }
    let newObj = obj
    obj = null
    console.log(newObj); // { name: 'dongsu' }

Map

Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。

把对象、函数、标准类型等作为键名

对象当中的键只能是字符串

Map类型增删改查操作

Map.set()

set()方法为Map对象添加或更新一个指定了键(key)和值(value)的(新)键值对

    let map = new Map()
    map.set('name', 'dongsu')
    map.set(1, 'daxie')
    console.log(map);

image.png

    let map1 = new Map([['name','dongsu'],['sex','girl']])
    // 链式操作
    map1.set('age', 1).set('hobby', 'swim')
    // 清晰明了为主
    console.log(map1);

image.png

Map.get()

返回某个Map对象中的一个指定元素

    let map = new Map()
    map.set('name', 'dongsu')
    console.log(map.get('name')); // dongsu
    let obj = {
      'age': 1
    }
    map.set(obj, 'sudong')
    console.log(map.get(obj)); // sudong

Map.delete

用于移除Map对象中指定的元素

    let map = new Map()
    map.set('name', 'dongsu')
    let obj = {
      'age': 1
    }
    map.set(obj, 'sudong')
    console.log(map.get(obj)); // sudong
    map.delete(obj)
    console.log('map', map); // map Map(1) {'name' => 'dongsu'}

Map.has

返回一个boolean值,用来表明map中是否存在指定元素

    let map = new Map()
    map.set('name', 'dongsu')
    let obj = {
      'age': 1
    }
    map.set(obj, 'sudong')
    console.log(map.get(obj)); // sudong
    map.delete(obj)
    console.log('map', map);
    console.log(map.has(obj)); // false
    console.log(map.has('name')); // true

遍历Map类型数据

    let map = new Map([['name','dongsu'],['age',16],['sex','boy']])
    console.log(map.keys());
    console.log(map.values());
    console.log(map.entries());

image.png

    let map = new Map([['name','dongsu'],['age',16],['sex','boy']])
    for(const key of map){
      console.log(key);
    }

image.png

    for(const key of map.keys()){
      console.log(key);
    }
    for(const value of map.values()){
      console.log(value);
    }
    for(const [key, value] of map.entries()){
      console.log(key, value);
    }

image.png

Map类型转换操作

        let hd = new Map([['name', 'ds'], ['age', '18']])
        console.log([...hd]);
        console.log([...hd.keys()]);
        console.log([...hd.values()]);
        console.log([...hd.entries()]);

        for (const [name, key] of hd) {
            console.log('123', name, key);
        }

        let newArr = [...hd].filter(item => {
            console.log('new', item[1]);
            return item[1].includes('ds')
        })
        console.log('newArr', newArr);
        let per = new Map(newArr)
        console.log(...per.values());

image.png

Map类型管理DOM节点

  <div>dongsu.com</div>
  <div>dscms.com</div>
  <script>
    // map存储Dom对象
    let map = new Map()
    document.querySelectorAll('div').forEach(item => {
      map.set(item, {
        content: item.getAttribute('name')
      })
    })
    map.forEach((config, elem) => {
      console.log(elem);
      elem.addEventListener("click", ()=>{
        console.log(config.content);
      })
    })
  </script>

Map在勾选协议情景下的小练习

    <form action="https://baidu.com" onsubmit="return post()">
    接受协议:
    <input type="checkbox" name="agreement" error="请接收协议">
    我是学生:
    <input type="checkbox" name="student" error="网站只对学生开放">
    <input type="submit">
  </form>
  <script>
    function post() {
      let map = new Map()
      let inputs = document.querySelectorAll('[error]')
      inputs.forEach(item => {
        map.set(item, {
          item: item.getAttribute('error'),
          itemStatus: item.checked
        })
      });
      return [...map].every(([item, config]) => {
        console.log(config);
        config.itemStatus || alert(config.item)
        return config.itemStatus
      })
    }
  </script>

image.png

WeakMap的使用

WeakMap弱引用类型

它的键必须是一个对象

    let map = new WeakMap()
    map.set('name', 'dongsu')

image.png

  <div>123</div>
  <div>344</div>
  <script>
    let divs = document.querySelectorAll('div')
    let map = new WeakMap()
    divs.forEach(item => {
      map.set(item, item.innerHTML)
    })
  </script>

image.png

set、delete、has

    let arr = []
    let map = new WeakMap()
    map.set(arr, 'dongsu')
    map.delete(arr)
    console.log('map', map);
    console.log('has', map.has(arr));
    console.log('keys', map.keys());

image.png

keys、for...of不能用

WeakMap弱引用

    // 引用类型引用一次,计数器加一,释放则减1
    let ds= {name: 'dongsu'}
    cms = ds
    // 弱类型引用,计算器无变化
    let map = new WeakMap()
    map.set(ds, 'dongsu.com')
    ds = null
    cms = null
    console.log(map);

image.png

函数

函数是对象,JS中函数也是对象函数是Function类的创建的实例

Function类

    // new Function(参数,函数体)
    let func = new Function('title', "console.log(title)")
    func('东酥')

标准语法

    // 字面量创建
    function ds(title){ // 具名函数
      console.log(title);
    }
    ds('dongsu')

对象字面量属性函数

    let user = {
      name: 'dongsu',
      getName: function() {
        return this.name
      },
      // 简写
      setName(value) {
        this.name = value
      }
    }

全局函数

函数尽量不要独立存放,使用类/模块

全局独立的函数如果命名与系统函数相同,会覆盖系统函数,导致没法使用

匿名函数

    let cms = function(title){
      console.log(title);
    }
    cms('dongsu')
    show() // 可以正常执行,是因为函数提升
    function show() {
      console.log('dongsu');
    }
    show() // 打印dongsu,因为函数存在提升,而匿名函数不会
    function show() {
      console.log('dongsu');
    }
    var show = function() {
      console.log('dongsu.com');
    }
    var show = function() {
      console.log('dongsu.com');
    }
    function show() {
      console.log('dongsu');
    }
    show() // dongsu.com

立即执行函数与块作用域解决冲突

  • 立即执行函数
// js1.js
    (function(window) {
    function ds() {
        console.log('ds-----js1');
    }
    function show() {
        console.log('show-----js1');
    }
    window.js1 = { ds, show }
})(window)
  • 块作用域
    {
    let ds = function () {
        console.log('ds-----js1');
    }
    let show = function() {
        console.log('show-----js1');
    }
    window.js1 = { ds, show}
    }
    <script src="./js1.js"></script>
    <script>
        /*
            引用别人的插件,可能存在命名冲突的问题
        */
        // 立即执行函数
        function ds(){
            console.log(123);
        }
        js1.ds()
    </script>

推荐使用模块化的方式

形参和实参

        function sum(a, b) {
            return a + b
        }
        console.log(sum(1, 3));

默认参数的使用

默认参数放在最后

        function avg(total, year = 1) {
            return Math.round(total / year)
        }
        console.log(avg(2000, 2));
        function sortArray(array, type = 'asc') {
            return array.sort(function (a, b) {
                return type === 'asc' ? a - b : b - a
            })
        }
        console.log(sortArray([3, 1, 7, 2]));

函数参数

函数的参数不受类型的约束

        var i = 0;
        function cms() {
            console.log(++i);
        }
        setInterval(cms, 1000)

arguments

        function sum(){
            console.log(arguments);
            console.log(arguments.length);
            // arguments.reduce不能用,因为arguments是伪数组
            return [...arguments].reduce((a, b) => a + b)
        }
        console.log(sum(1,4,5,6,7,88));

image.png

箭头函数

        let dongsu = function() {
            return 'dong' + 'su'
        }
        console.log(dongsu());
        let ds = () => 'dong' + 'su'
        console.log(ds());

不适用箭头函数:

  • 递归函数
  • 构造函数
  • 事件处理

使用函数实现递归算法

        function factorial(num) {
            return num == 1 ? num : num * factorial(num - 1)
        }
        console.log(factorial(5));
    function sum(...args){
            return args.length === 0 ? 0 : args.pop() + sum(...args)
        }
        console.log(sum(1,3,2,44));

递归实现倒三角

        function star(num){
           return num ? document.write('*'.repeat(num) + '<br />') || star(--num) : ''
        }
        star(5)

回调函数

简要理解:在其他函数中调用的函数

        let ds = [1, 2, 3, 4]
        ds.map(function(value, index, array){
            array[index] += 10
            return value
        })
        console.table(ds)

展开语法(点语法)

        let ds = [1, 2, 3, 4]
        let [a, b, c] = [...ds]
        console.log(a, b, c); // 1,2,3
        console.log([...ds]); // [1,2,3,4]
        let [g, ...num] = [1, 2, 3, 4]
        console.log(num); // [2,3,4]
        function sum(num, ...args){
            console.log(args); // [2,3,4]
        }
        console.log(sum(1,2,3,4));

展开语法放在最后

函数和方法中this的不同

    let ds = {
      name: 'dongsu',
      show: function () {
        console.log(this.name); // this当前的对象
        console.log(this);
        function render() {
          console.log(this); // window
        }
        render() // window.render()
      }
    }
    ds.show()
    // this 指向引用的环境

通过常量改变this指针

    let lesson = {
      site: '东苏',
      lists: ['js', 'css', 'vue', 'react'],
      show: function(){
        const self = this
        return this.lists.map(function(value){
          return `${self.site}-${value}`
        })
      }
    }
    console.log(lesson.show());
    let lesson = {
      site: '东苏',
      lists: ['js', 'css', 'vue', 'react'],
      show: function(){
        return this.lists.map(function(value){
          return `${this.site}-${value}`
        }, this)
      }
    }
    console.log(lesson.show());

箭头函数中的this

    <button>search</button>
    <script>
        let ds = function () {
            console.log('东酥');
        }
        ds()
        // 箭头函数的this指向上下文(父级作用当中的this)
        // 普通函数的this指向window
        // 如果函数是一个属性的值,那么this是这个对象
        let Dom = {
            site: '东酥',
            bind: function () {
                console.log(this);
                const button = document.querySelector('button')
                // button.addEventListener('click', () => {
                //     console.log(this); // {site:'东酥', bind: f}
                // })
                button.addEventListener('click', function () {
                    console.log(this); // button
                })
                // =======>
                // button.click = function() {
                //     console.log('this', this);
                // }
            }
        }
        Dom.bind()
    </script>

this构造原理实现

call

改变this指向,立即执行

        function User(name){
            console.log(this); // User {}
            this.name = name
        }
        let lisi = new User('李四')
        console.log(lisi);
        let hscms = { url: 'dongsu.com' }
        User.call(hscms, '123') // User的this指向第一个参数hscms,第二个参数作为User的形参
        console.log(hscms);

apply

改变this指向,立即执行

        let zhangsan = {
            name: '张三'
        }
        let wangwu = {
            name: '王五'
        }
        function User(web, url) {
            console.log(this);
            console.log(web + url + this.name);
        }
        User.call(zhangsan, 'dongsu', 'dongsu.com'); // 改变this指向,立即执行
        User.apply(zhangsan, ['dongsu', 'dongsu.com']); // 改变this指向,立即执行

apply与call的区别就是参数的格式

        // 求最大值
        console.log(Math.max(12, 3, 4, 55));
        let arr = [1, 3, 6, 2]
        console.log(Math.max(...arr));
        console.log(Math.max.apply(Math, arr));

Math.max.apply(Math, arr),这里不能使用call,call会把参数当成一个整体(会把数组直接传递,Math.max会报NaN)

收缩折叠面板的应用

    <dl>
        <dt>东酥</dd>
        <dd>1</dd>
        <dt>dongsu</dd>
        <dd hidden="hidden">2</dd>
    </dl>
    <script>
        function panel(index) {
            var dds = document.querySelectorAll('dd')
            dds.forEach(dd => {
                dd.setAttribute('hidden', 'hidden')
            })
            dds[index].removeAttribute('hidden')
        }
        var dts = document.querySelectorAll('dt')
        dts.forEach((dt, index) => {
            dt.addEventListener('click', () => {
                // panel(index)
                panel.call(null, index)
            })
        })
    </script>
     <style>
        * {
            padding: 0;
            margin: 0;
        }

        dt {
            background-color: orange;
            text-align: center;
            width: 200px;
        }

        dd {
            background-color: #eee;
            height: 200px;
            text-align: center;
            width: 200px;
        }

        .hidden {
            display: none;
        }
    </style>

call不一定要改变this,可以传null

bind

bind不会立即执行,使用bind改变this指向,它会得到新的函数

        function show() {
            console.log(this.name);
        }
        show.apply({ name: 'ss' })
        // bind不会立即执行,使用bind改变this指向,它会得到新的函数
        show.bind({ name: 'dong' })()
        let a = function () {}
        let b = a
        console.log(a instanceof Object); // true
        console.log(a === b); // true
        b = a.bind()
        console.log(a === b); // false

bind参数

     // 参数传递
        function ds(a,b){
            return this.f + a + b
        }
        // console.log(ds.call({f:1}, 1, 1)); // 3
        let funF = ds.bind({f: 1}, 2, 2) // 先从bind第二个参数查找是否有参数,如果有直接使用,如果没有就去新函数的参数中查找
        console.log(funF(2,4)); // 如果bind的参数没有,就在新产生的函数中查找参数

image.png

image.png

image.png

bind应用

    <button>好好学习</button>
    <script>
        document.querySelector('button').addEventListener('click', function(event) {
            document.write(this.url + event.target.innerHTML)
        }.bind({url: 'dongsu.com'}))
    </script>

addEventListener的function不是立即执行

        function Color(elem) {
            this.elem = elem
            this.colors = ['red', 'blue', 'pink']
            this.run = function(){
                // setInterval(() => {
                //     console.log(this);
                //     let i = Math.floor(Math.random() * this.colors.length)
                //     console.log(this.elem);
                //     this.elem.style.background = this.colors[i]
                // }, 1000);
                setInterval(function(){
                    console.log(this);
                    let i = Math.floor(Math.random() * this.colors.length)
                    console.log(this.elem);
                    this.elem.style.background = this.colors[i]
                }.bind(this), 1000);
            }
        }
        let obj = new Color(document.body)
        obj.run()

环境与作用域

  • 全局环境 定义的数据会被保留、不会被卸载、不会被回收 除非关闭浏览器、人为去操作

函数每调用一次,就产生一个内存空间,调用完成就清除内存

image.png

数据在被使用,就不会被清除掉

    function hd(){
      let n = 1;
      return function sum() {
        console.log(n++);
      }
    }
    let a = hd()
    a()

image.png

image.png

image.png

    function DS(){
      let n = 1;
      this.sum = function(){
        console.log(++n);
      }
    }
    let a = new DS()
    a.sum() // 2
    a.sum() // 3

函数在被使用,同作用域下的内容也被保留

var、let、const

    for(var i = 1; i <= 3; i++){
      console.log(i);
    }
    console.log(i); // window.i

image.png

    for(let i = 1; i <= 3; i++){
      console.log(i);
    }
    console.log(i); // window.i

image.png

    for(var i = 1; i <= 3; i++){
      setTimeout(() => {
        console.log(i);
      }, 1000);
    }
    console.log(i); // window.i

image.png

    for(let i = 1; i <= 3; i++){
      setTimeout(() => {
        console.log(i);
      }, 1000);
    }
    console.log(i); // window.i

image.png

    for (var i = 1; i <= 3; i++) {
      (function (a) {
        setTimeout(() => {
          console.log(a);
        }, 1000);
      })(i)
    }
    console.log(i); // window.i

image.png

    let arr = []
    for (var i = 1; i <= 3; i++) {
      arr.push(function(){
        return i;
      })
    }
    console.log(arr[0]()); // 4
    console.log(arr[1]()); // 4
    console.log(arr[2]()); // 4
    let arr = []
    for (var i = 1; i <= 3; i++) {
      (function (i) {
        arr.push(function () {
          return i;
        })
      })(i)
    }
    console.log(arr[0]()); // 1
    console.log(arr[1]()); // 2
    console.log(arr[2]()); // 3
    let arr = []
    for (let i = 1; i <= 3; i++) {
      arr.push(function(){
        return i;
      })
    }
    console.log(arr[0]()); // 1
    console.log(arr[1]()); // 2
    console.log(arr[2]()); // 3

image.png

闭包

函数能访问到其他函数作用域当中的数据

    function ds (){
      let n = 1;
      return function sum(){
        console.log(++n);
      }
    }
    let a = ds()
    a() // 2
    a() // 3

闭包的内存泄露解决方法

    <div desc="online">在线学习</div>
    <div desc="progress">不断进步</div>
    <script>
        let divs = document.querySelectorAll('div')
        divs.forEach(function(item){
            item.addEventListener('click', function(){
                console.log(item.getAttribute('desc'));
            })
        })
    </script>

上面存在的问题,点击只是为了获得desc属性的值,而这里直接使用item,使得整个DOM都保存在内存当中,导致内存空间的占用

    <div desc="online">在线学习</div>
    <div desc="progress">不断进步</div>
    <script>
        let divs = document.querySelectorAll('div')
        divs.forEach(function(item){
            let desc = item.getAttribute('desc')
            item.addEventListener('click', function(){
                // console.log(item.getAttribute('desc'));
                console.log(desc);
                console.log(item);
            })
            item = null
        })
    </script>

this在闭包中的历史遗留问题

        let hd = {
            user: '后盾人',
            get: function(){
                return function() {
                    return this.user // this指向window
                }
            }
        }
        let a = hd.get()
        console.log(a()); // undefined 外部调用指向window
        let hd = {
            user: '后盾人',
            get: function(){

                return () => {
                    return this.user // this指向hd
                }
            }
        }
        let a = hd.get()
        console.log(a()); // 后盾人

对象

函数变成与面向对象的实例对比

传统的函数编程会有错中复杂的依赖很容易创造意大利式面条代码

面条式代码:是非结构化和难以维护的源代码的贬义词组,广泛地解释。 意大利面条代码可能由多种因素引起,例如易变的项目要求,缺乏编程风格规则以及能力或经验不足

属性的基本操作

  • 获取 属性是变量变量、属性中特殊的符号(如空格)

对象[属性名]

  • 设置

对象.属性名

  • 删除

delete 对象.属性名/对象[属性名]

        let user = {
            name: '东酥',
            "my age": 18
        }
        console.log(user["my age"]); // 18
        user.get = function(){
            return `${this.name}的年龄是${this['my age']}`
        }
        console.log(user.get());

对象是引用类型

        let user = { name: '东酥'}
        function show(user) { 
            user.age = 18 // 传的是复杂类型的数据的地址,所以当修改时会直接修改复杂数据,所有引用的地方都会被修改
            console.log(user);
        }
        show(user)
        console.log(user); // {name: '东酥', age: 18}

使用展开语法完成参数合并

        let user = { name:'东酥', age: 22}
        let ds = { ...user, hobby: 'eating' }
        console.log(ds); // {name: '东酥', age: 22, hobby: 'eating'}

解构

解构赋值新增特性

对结构的分解处理

        let user = { name: '东酥', age: 18 }
        let { name: a, age } = user
        console.log('name:', a,'age:', age); // name: 东酥 age: 18

严格模式下解构的差异

"use strict"

        let user = { name: '东酥', age: 18 };
        ({ name: a, age } = user)
        console.log('name:', a,'age:', age);
        "use strict"
        let user = { name: '东酥', age: 18 };
        ({ name: a, age } = user)
        console.log('name:', a,'age:', age);

image.png

解构操作的简写形式与变量解构

        /* 
            let {原属性名:新属性名} = 对象
            如果原属性名与新属性名一致,就可以简写为
            let {属性名} = 对象
        */
        let user = { name: '东酥', age: 18 }
        let { name, age } = user
        console.log(name, age);

解构默认值实现配置下合并

值存在就不使用默认值,不存在则使用默认值

        let arr = ['东酥', 'dongsu']
        let [a, b, c = '我是默认值'] = arr
        console.log(a, b, c); // 东酥 dongsu 我是默认值
        let arr = ['东酥', 'dongsu', 'ccc']
        let [a, b, c = '我是默认值'] = arr
        console.log(a, b, c); // 东酥 dongsu ccc

函数参数中使用解构

        function ds(name, { sex, age }) {
            console.log(name, sex, age); // 东酥 男 18
        }
        ds('东酥', { sex: '男', age: 18 })
         function ds(name, { sex: DSex, age: Dage }) {
            console.log(name, DSex, Dage); // 东酥 男 18
        }
        ds('东酥', { sex: '男', age: 18 })

对象属性的添加删除操作

检测属性:hasOwnProperty

hasOwnProperty检测对象自身是否包含指定的属性,不检测原型链上继承的属性

    /**
         * hasOwnProperty: 判断当前对象上有没有属性
        */
       let ds = {}
       ds.name = '东酥'
       ds['age'] = 18
       console.log(ds);
       delete ds.name
       console.log(ds.hasOwnProperty('age')); // true

in 可以在原型对象上检测

        let ds = { name: '东酥' }
        let arr = ['dongsu', '东酥']
        console.log(arr.hasOwnProperty('length')); // true length存在于数组对象当中
        console.log(arr.hasOwnProperty('concat')); // false
        // in 不仅检测自己,还检测父级
        console.log('length' in arr); // true
        console.log('concat' in arr); // true

setPrototypeOf

Object.setPrototypeOf(obj, prototype)

方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或null

  • obj: 要设置其原型的对象。.

  • prototype: 该对象的新原型(一个对象 或 null).

        let a = {
            name: '东酥'
        }
        let b = {
            url: 'dongsu.com'
        }
        Object.setPrototypeOf(a, b)
        console.log(a);
        console.log(a.hasOwnProperty('url')); // false
        console.log('url' in a); // true

image.png

计算属性与assign使用

    let lessons = [
      {
        title: 'JS高级内容',
        category: 'JS'
      },
      {
        title: 'css样式',
        category: 'css'
      },
      {
        title: 'vue深入浅出',
        category: 'Vue'
      }
    ]
    let res = lessons.reduce((obj, cur, index) => {
      obj[`${cur["category"]}-${index}`] = cur
      return obj
    }, {})
    console.log(JSON.stringify(res, null, 2));

image.png

assign

    let ds = Object.assign({name:'东书'}, {age: 18})
    console.log(ds);// {name: '东书', age: 18}

对象的浅拷贝

Object.assign()拷贝

当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。

对象的深拷贝

递归

    function copy (obj) {
      let res = obj instanceof Array ? [] : {}
      for (const [k, v] of Object.entries(obj)) {
        res[k] = typeof v == 'object' ? copy(v) : v
      }
      return res
    }

工厂函数创建对象

构造函数创建对象

属性的特性

封闭对象

Object.seal()

封闭之后不能添加属性,不能修改

Object.isSealed()

是否处于封闭状态

冻结属性

Object.freeze()

不能添加、删除、修改、遍历

Object.isFrozen()

是否处于冻结状态

访问器

    const user = {
      data: { name: '后盾人', age: 10 },
      set age(value) {
        console.log(value);
        if (typeof value != "number" || value < 10 || value > 100) {
          throw new Error("年龄格式错误")
        }
        this.data.age = value
      },
      get age() {
        return this.data.age + '岁'
      }
    }
    user.age = 99 

访问器伪造属性操作

    let lesson = {
      lists: [
        {name:'js',price: 100},
        {name:'mysql',price: 200},
        {name:'vue',price: 300}
      ],
      get total() {
        return this.lists.reduce((t,l)=>t + l.price, 0)
      }
    }
    console.log(lesson.total); // 600
    lesson.total = 999
    console.log(lesson.total); // 600

使用访问器批量设置属性

    const web = {
      name: '东酥',
      url: 'dongsu.com',
      get site() {
        return `${this.name}的网址是${this.url}`
      },
      set site(value) {
        [this.name, this.url] = value.split(',')
      }
    }
    web.site = "开源产品,www.baidu.com"
    console.log(web.site);
    let Request = {
      set token(content) {
        sessionStorage.setItem('token', content)
      },
      get token() {
        let token = sessionStorage.getItem('token')
        if (!token) {
          alert('请登录')
        }
        return token
      }
    }
    Request.token = '232194932848128'
    console.log(Request.token);

访问器的优先级

    const user = {
      name: '东酥',
      age: 10,
      set name(value) {
        console.log(value + '-forever');
      }
    }
    user.name = "dongsu"
    console.log(user); // {age: 10}

image.png

说明访问器的优先级更高

    const DATA = Symbol() // 唯一,保护数据
    const user = {
      [DATA]: { name },
      age: 10,
      set name(value) {
        this[DATA].name = value
      },
      get name() {
        return this[DATA].name
      }
    }
    user.name = 'dongsu'
    console.log(user[Symbol()]); // undefined

构造函数与class语法糖中使用访问器

        function User(name, age) {
            this.name = name;
            this.age = age;

        }
        let ds = new User('东酥', 18)
        ds.name = '小傻逼'

上面这种写法,数据可被随意的更改

        function User(name, age) {
            let data = { name, age }
            Object.defineProperties(this, {
                name: {
                    get() {
                        return data.name
                    },
                    set(value) {
                        if (value.trim() !== '' || value.length > 20){
                            return new Error('用户名不合法')
                        }
                        data.name = value
                    }
                },
                age: {
                    get() {
                        return data.age
                    },
                    set(value) {
                        data.age = value
                    }
                }
            })
        }
        let ds = new User('东酥', 18)
        ds.name = 'fff'
        console.log(ds.name); // 东酥
        const DATA = Symbol() // 唯一,保护数据不为修改
        class User {
            constructor(name, age) {
                this[DATA] = { name, age }
            }
            get name() {
                return this[DATA].name
            }
            set name(value) {
                if (value.trim() !== '' || value.length > 20) {
                    return new Error('用户名不合法')
                }
                this[DATA].name = value
            }
            get age() {
                return this[DATA].age
            }
            set age(value) {
                this[DATA].age = value
            }
        }
        let ds = new User('东酥', 18)
        ds.name = 'fff'
        console.log(ds.name); // 东酥
        console.log(ds);

Proxy代理拦截

  • 访问器对单个属性进行控制
  • 对象代理是对整个对象进行控制
    const ds = { name: '东酥', age: 18 }
       const proxy = new Proxy(ds, {
           get(obj, property) {
               return obj[property]
           },
           set(obj, property, value) {
               obj[property] = value
           }
       })
       console.log(proxy.name);
       console.log(proxy.age);
  • 代理Proxy控制函数
        function factorial(num) {
            return num == 1 ? 1 : num * factorial(num - 1)
        }
        let res = factorial(5)
        console.log(res); // 120
        function factorial(num) {
            return num == 1 ? 1 : num * factorial(num - 1)
        }
        let proxy = new Proxy(factorial, {
            apply(func, obj, args) {
                /**
                 * func 函数
                 * obj 当前上下文的对象 this
                 * args 参数
                */
                console.time('run')
                console.log(func, obj, args);
                console.timeEnd('run')
            }
        })
        // proxy.apply(this, [5])
        proxy.apply({}, [5])

数组使用代理拦截操作

        let lessons = [
            {
                title: 'css',
                type: 'css'
            },
            {
                title: 'Mysql',
                type: 'mysql'
            },
            {
                title: 'flex布局',
                type: 'css'
            },
            {
                title: 'vue',
                type: 'vue.js'
            },
        ]
        let proxy = new Proxy(lessons, {
            get(arr, key) {
                console.log(arr, key);
                const title = arr[key].title
                const len = 5
                arr[key].title = title.length > len ? title.substr(0, len) + '.'.repeat(3) : title
                return arr[key]
            }
        })
        // console.log(proxy[0]);
        console.log(JSON.stringify(proxy[1], null, 2));

VUEJS数据绑定的容器更新

    <input type="text" v-model="title">
    <input type="text" v-model="title">
    <div v-bind="title">这里也会发生更新</div>
    <script>
        function View() {
            let proxy = new Proxy({}, {
                get(obj, property) {},
                set(obj, property, value) {

                }
            })
            this.init = function() {
                const els = document.querySelectorAll('[v-model]')
                els.forEach(item => {
                    item.addEventListener('keyup', function () {
                        proxy[this.getAttribute('v-model')] = this.value
                    })
                })
            }
        }
        new View().init()
    </script>

双向数据绑定的页面渲染

    <input type="text" v-model="content">
    <h4 v-bind="content"></h4>
    <hr>
    <input type="text" v-model="title">
    <input type="text" v-model="title">
    <div v-bind="title">这里也会发生更新</div>
    <script>
        function View() {
            let proxy = new Proxy({}, {
                get(obj, property) { },
                set(obj, property, value) {
                    document.querySelectorAll(`[v-model="${property}"]`).forEach(item => {
                        item.value = value
                    })
                    document.querySelectorAll(`[v-bind="${property}"`).forEach(item => {
                        item.innerHTML = value
                    })
                }
            })
            this.init = function () {
                const els = document.querySelectorAll('[v-model]')
                els.forEach(item => {
                    item.addEventListener('keyup', function () {
                        proxy[this.getAttribute('v-model')] = this.value
                    })
                })
            }
        }
        new View().init()
    </script>

表单验证组件的代理工厂

    <style>
        .error {
            border: 1px solid red;
        }
    </style>
    <input type="text" validate rule="max: 12,min: 3">
    <input type="text" validate rule="max: 3,isNumber">
    <script>
        // 表单验证体验代理
        class Validate {
            max(value, len) {
                return value.length <= len
            }
            min(value, len) {
                return value.length >= len
            }
            isNumber(value) {
                return /^\d+$/.test(value)
            }
        }
        function ProxyFactory(target) {
            return new Proxy(target, {
                get(target, key) {
                    return target[key]
                },
                set(target, key, el) {
                    console.log(el);
                    const rule = el.getAttribute("rule")
                    const validate = new Validate()
                    let state = rule.split(",").every(rule => {
                        const info = rule.split(":")
                        return validate[info[0]](el.value, info[1])
                    })
                    // 根据状态来修改样式
                    el.classList[state ? 'remove' : 'add']('error')
                    return true // 严格模式下,要return
                }
            })
        }
        const proxy = ProxyFactory(document.querySelectorAll('[validate]'))
        proxy.forEach((item, i) => {
            console.log(item);
            item.addEventListener('keyup', function () {
                proxy[i] = this
            })
        });
    </script>

JSON数据

  • json 是一种轻量级的数据交换格式,易于人阅读和编写。
  • 使用json 数据格式是替换 xml 的最佳方式,主流语言都很好的支持json 格式。所以 json 也是前后台传输数据的主要格式。
  • json 标准中要求使用双引号包裹属性,虽然有些语言不强制,但使用双引号可避免多程序间传输发生错误语言错误的发生
        let data = {
            name: '东酥',
            data: {
                title: 'php'
            }
        }
        let json = JSON.stringify(data, null, 2)
        console.log(json);
        let obj = JSON.parse(json)
        console.log('obj', obj);

JSON序列化与自定义toJSON

序列化是将 json 转换为字符串,一般用来向其他语言传输使用

     let ds = {
            name: '东酥',
            url: 'www.baidu.com',
            teacher: {
                name: '向军大叔'
            },
            // 为数据添加 toJSON 方法来自定义返回格式
            toJSON: function() {
                return {
                    name: this.name
                }
            }
        }
        /**
         * JSON.stringify 转化为JSON格式
         * 参数一:需要转为JSON的数据
         * 参数二: 指定保存的属性
         * 参数三:Tab制表位(处理JSON的展示效果)
        */
        let json = JSON.stringify(ds)
        console.log(json);
        let arr = ['东酥', 'dongsu']
        let arrJSON = JSON.stringify(arr, ['name', 'en'], 2)
        console.log(arrJSON);

image.png

toJSON: 自定义返回格式

JSON转JS

反序列化

使用第二个参数函数来对返回的数据二次处理

    let ds = {
            name: '东酥',
            url: 'www.baidu.com',
            teacher: {
                name: '向军大叔'
            }
        }
        let json = JSON.stringify(ds, null, 2)
        console.log(json);
        let obj = JSON.parse(json, (key, value) => {
            // console.log('parse', key, value);
            if (key == 'name') {
                value = '人名' + value
            }
            return value
        })
        console.log(obj);

image.png

原型

初步认识

        let hd = {}
        console.log(Object.getPrototypeOf(hd));
        let xj = {}
        console.log(Object.getPrototypeOf(xj));
        console.log(Object.getPrototypeOf(hd) === Object.getPrototypeOf(xj));// true

image.png

没有原型的对象

Object.create()

    let xj = { name: '向军' }
        console.log(xj);
        console.log(xj.hasOwnProperty("name"));
        /**
         * Object.create
         * 第一个参数:指定父级(原型)
        */
        // 完全数据字典对象
        let ds = Object.create(null, {
            name: {
                value: 'dongsu'
            }
        })
        console.log(ds);

image.png

原型方法和对象方法的优先级

        let ds = {
            show(){
                console.log('我本身的方法');
            }
        }
        ds.__proto__.show = function(){
            console.log('原型中的方法');
        }
        console.log(ds.__proto__);
        ds.show() // 我本身的方法

image.png

函数的原型

  • 服务于函数实例化
        function User() { }

        User.prototype.show = function() {
            console.log('show-----');
        }
        // User.apply
        let hd = new User()
        hd.show()

        console.log(User.prototype === hd.__proto__);
        console.dir(User);

image.png

image.png

  • 服务于函数对象
        function User() {}
        User.__proto__.view = function () {
            console.log('user function user methods');
        }
        User.view()
        console.dir(User);

image.png

        let hd = new Object()
        hd.name = '后端人'
        // console.dir(Object)
        Object.prototype.show = function() {
            console.log('houdunren.com');
        }
        // hd.show()
        function User(){}
        console.dir(User);
        console.log(User.prototype.__proto__ === User.__proto__.__proto__);
        // console.dir(Object.prototype.__proto__); // null

image.png

image.png

image.png

        let hd = new Object()
        hd.name = '后端人'
        // console.dir(Object)
        Object.prototype.show = function() {
            console.log('houdunren.com');
        }
        // hd.show()

        function User(){}
        User.show() // houdunren.com
        let hd = new Object()
        hd.name = '后端人'
        // console.dir(Object)
        Object.prototype.show = function() {
            console.log('houdunren.com');
        }
        // hd.show()

        function User(){}
        let xj = new User()
        xj.show() // houdunren.com

系统构造函数的原型体现

        let obj = {}
        console.log(obj.__proto__ === Object.prototype); // true

        let arr = []
        console.log(arr.__proto__ === Array.prototype); // true

        let str = ''
        console.log(str.__proto__ === String.prototype); // true

        let bool = true
        console.log(bool.__proto__ === Boolean.prototype); // true

        let reg = /a/i
        console.log(reg.__proto__ === RegExp.prototype); // true

修改构造函数原型上面的方法,后面继承的元素都会受到影响

自定义对象的原型设置

        let hd = { name: 'ds' }
        let parent = {
            name: 'parent', 
            show() {
                console.log('parent', this.name); // this:谁调用就是谁
            }
        }
        console.log(hd.__proto__ === Object.prototype);
        Object.setPrototypeOf(hd, parent)
        console.log(hd);
        hd.show() // ds
        parent.show() // parent
        console.log(Object.getPrototypeOf(hd));
        console.log(Object.getPrototypeOf(parent));

image.png

原型中constructor的引用

        function User(name) {
            this.name = name
        }
        // 直接给prototype赋值(这种方式可以添加多个内容),会改变原型
        // User.prototype = {
        //     // constructor: User, // 如果直接给prototype赋值,会改变原型,需要指定constructor
        //     show(){
        //         console.log(this.name);
        //     }
        // }

        User.prototype.show = function(){
            console.log(this.name);
        }
        console.dir(User);
        console.log(User.prototype.constructor === User); // true
        let lisi = new User.prototype.constructor('李四')
        lisi.show() // 李四

image.png

根据一个对象再生成一个新的对象

        function User(name) {
            this.name = name;
            this.show = function(){
                console.log(this.name);
            }
        }
        User.prototype = {
            constructor: User,
            show() {
                console.log(this.name)
            }
        }
        let ds = new User('东酥')
        console.log('ds', ds);
        function createByObject(obj, ...args) {
            const constructor = Object.getPrototypeOf(obj).constructor
            // constructor === User
            return new constructor(...args)
        }
        let Ds = createByObject(ds, '小傻逼')
        console.log('Ds', Ds);
        Ds.show()

原型链

        let arr = [] // new Array
        console.log(arr.__proto__);
        console.log(Object.getPrototypeOf(arr));
        console.log(arr.__proto__.__proto__ === Object.prototype);
        console.log(Object.prototype.__proto__); // null


        let a = { name: '东酥' }
        let b = {
            name: 'b',
            show() {
                console.log(this.name);
            }
        }
        Object.setPrototypeOf(a, b)
        a.show()

image.png

image.png

image.png

原型链检查之instanceof

构造函数的prototype是否在另一个对象的原型链上

        function A() { }
        function B() { }
        let b = new B()
        A.prototype = b
        let a = new A()
        // a instanceof A: a的原型链上是否有A.prototype
        console.log(a instanceof A);
        console.log(a instanceof Object);
        console.log(a instanceof B);

image.png

        function A() { }
        function B() { }
        function C() { }
        let c = new C()
        B.prototype = c
        let b = new B()
        A.prototype = b
        let a = new A()
        // a instanceof A: a的原型链上是否有A.prototype
        console.log(a instanceof A); // true
        console.log(a instanceof Object); // true
        console.log(a instanceof B); // true
        console.log(a instanceof C); // true
        console.log(b instanceof A); // false

image.png

Object.isPrototypeOf原型检测

一个对象是否在另一个对象的原型链上

       // Object.prototype = b.__proto__
       let a = {}
       let b = {}
       console.log(Object.prototype.isPrototypeOf(a)); // true
       console.log(b.__proto__.isPrototypeOf(a)); // true
       console.log(b.isPrototypeOf(a)); // false
       // Object.prototype = b.__proto__
       let a = {}
       let b = {}
       let c = {}
       Object.setPrototypeOf(b, c)
       Object.setPrototypeOf(a, b)
       console.log(Object.prototype.isPrototypeOf(a)); // true
       console.log(b.__proto__.isPrototypeOf(a)); // true
       console.log(b.isPrototypeOf(a)); // true
       console.log(c.isPrototypeOf(b)); // true
       console.log(c.isPrototypeOf(a)); // true

in和hasOwnProperty的属性检测

in

检测属性是否在对象或对象的原型链上

        let a = { url: 'https://ww.baidu.com' }
        let b = { name: '百度' }
        Object.prototype.web = 'dongsu'
        console.log('url' in a); // true
        console.log('web' in a); // true
        console.log('name' in a); // false
        Object.setPrototypeOf(a, b)
        console.log('name' in a); // true

hasOwnProperty

仅仅检测对象本身,不会检测原型链

        let a = { url: 'https://ww.baidu.com' }
        let b = { name: '百度' }
        Object.setPrototypeOf(a, b)
        console.log(a.hasOwnProperty('name')); // false
        console.log(a.hasOwnProperty('url')); // true
        for (const key in a) {
            console.log(key);
        }
        for (const key in a) {
            if (Object.hasOwnProperty.call(a, key)) {
                const element = a[key];
                console.log('a-el', element);
            }
        }

image.png

call和apply借用原型链

apply

        let ds = {
            data: [1, 3, 5, 6, 67, 7]
        }
        Object.setPrototypeOf(ds, {
            max() {
                return this.data.sort((a, b) => b - a)[0]
            }
        })
        console.log(ds.max());

        let sd = {
            lessons: { js: 88, php: 90, node: 99, linux: 98 },
            get data() {
                return Object.values(this.lessons)
            }
        }
        console.log(ds.max.apply(sd));

call

        let ds = {
            data: [1, 3, 5, 6, 67, 7]
        }
        Object.setPrototypeOf(ds, {
            max(data) {
                return data.sort((a, b) => b - a)[0]
            }
        })
        console.log(ds.max(ds.data));

        let sd = {
            lessons: { js: 88, php: 90, node: 99, linux: 98 }
        }
        console.log(ds.max.call(null, Object.values(sd.lessons)));

优化

        /**
         * Math.max(1, 3, 5, 6, 67, 7)
         * 传递多个参数:使用apply
        */
        let ds = {
            data: [1, 3, 5, 6, 67, 7]
        }
        console.log(Math.max.apply(null, ds.data));
        let sd = {
            lessons: { js: 88, php: 90, node: 99, linux: 98 }
        }
        console.log(Math.max.apply(null, Object.values(sd.lessons)));
        /**
         * Math.max(1, 3, 5, 6, 67, 7)
         * 传递多个参数:使用apply
         * call的参数作为一个整体传递
        */
        let ds = {
            data: [1, 3, 5, 6, 67, 7]
        }
        console.log(Math.max.call(null, ...ds.data));
        let sd = {
            lessons: { js: 88, php: 90, node: 99, linux: 98 }
        }
        console.log(Math.max.call(null, ...Object.values(sd.lessons)));

DOM节点借用Array原型方法

    <button message='东酥' class="red">东酥</button>
    <button message='dongsu'>dongsu</button>
    <script>
        let arr = [1,2,4]
        let res = arr.filter(item => {
            return item > 39
        })
        // console.dir(Array.prototype.filter)

        let btns = document.querySelectorAll('button')
        // DOM数组是伪元素
        // btns = [].filter.call(btns, item => {
        //     return item.hasAttribute('class')
        // })
        // ===========>
        btns = Array.prototype.filter.call(btns, item => {
            return item.hasAttribute('class')
        })
        console.log('btns', btns);
        console.log(btns[0].innerHTML);
    </script>

合理的构造函数方法声明

        function User(name) {
            this.name = name;
            this.show = function() {
                console.log(this.name);
            }
        }
        let lisi = new User('李四')
        let ds = new User('东酥')
        console.log('lisi', lisi);
        console.log('ds', ds);

image.png

同一个方法,开辟的两个空间都占用了,内存浪费

        function User(name) {
            this.name = name;
        }
        User.prototype.show = function () {
            console.log(this.name);
        }
        let lisi = new User('李四')
        let ds = new User('东酥')
        console.log('lisi', lisi);
        console.log('ds', ds);

image.png

设置在原型上面,只占用一份进行复用

        function User(name) {
            this.name = name;
        }
        // User.prototype.show = function () {
        //     console.log(this.name);
        // }
        // User.prototype.get = function () {
        //     console.log('get.....');
        // }
        User.prototype = {
            constructor: User,
            show(){
                console.log(this.name);
            },
            get(){
                console.log('get.....');
            }
        }
        let lisi = new User('李四')
        let ds = new User('东酥')
        console.log('lisi', lisi);
        console.log('ds', ds);
        lisi.show()
        lisi.get()

this和原型的关系

this和原型没什么关系

        let ds = {
           name: '东酥'
       }
       let User = {
           name: '后盾人',
           show() {
               console.log(this.name);
           }
       }
       Object.setPrototypeOf(ds, User)
       console.log(ds.show()); // 东酥 this指向调用的对象 

不要滥用原型

    <button onclick="this.hide()">点击</button>
    <script>
        Object.prototype.hide = function() {
            this.style.display = 'none'
        }
    </script>

强烈不建议在系统的原型中追加方法,相同命名会被覆盖,导致其非常不稳定

Object.create和__proto__

        let user = {
            show() {
                return this.name;
            }
        }
        // prototype
        // 为单个对象更改原型
        // 定义对象的原型,不能获取
        let ds = Object.create(user, {
            name: {
                value: '小东东'
            }
        })
        // ds.name = "东酥"
        console.log(ds.show());
        let user = {
            show() {
                return this.name;
            }
        }
        // __proto__ 可以设置,也可以获取
        let ds = { name: '东酥' }
        ds.__proto__ = user
        console.log(ds.show());
        console.log(ds.__proto__);

使用setPrototypeOf替代__proto__

Object.setPrototypeOf(参数一,参数二)

参数一继承参数二的原型

        let user = {
            show() {
                return this.name;
            }
        }
        let ds = {
            name: '东酥'
        }
        Object.setPrototypeOf(ds, user)
        console.log(ds.show());
        console.log(Object.getPrototypeOf(ds));

Object.getPrototypeOf(参数一):获取参数一的原型

__proto__是非标准的

__proto__原来是属性访问器

       let ds = { name: '东酥'}
       ds.__proto__ = {
           show() {
               console.log(this.name);
           }
       }
       ds.__proto__ = 99
       ds.show() // 东酥
       console.log(ds.__proto__);

__proto__的setter做了设置

仿写

        let ds = {
            action: {},
            get proto() {
                return this.action
            },
            set proto(obj) {
                if (obj instanceof Object) {
                    this.action = obj
                }
            }
        }
        ds.proto = { view: function(){}}
        ds.proto = 'abc'
        console.log(ds.proto);

image.png

非要使用__proto__去修改数据

        // 创建没有原型的数据
        let ds = Object.create(null)
        console.dir(ds)
        ds.__proto__ = '东酥'
        console.dir(ds.__proto__)

image.png


学习笔记