Web前端第三阶段--JsCode

Web前端第三阶段–JsCode



声明提升

  • 声明:在内存中创建一个内存块,然后起个名字
  • JS引擎先通篇扫描声明操作, 提升(移动)到作用域的顶部然后再执行调整过顺序后的代码

面试题

      function a() {
        console.log(1)
      }
      a()
      function a() {
        console.log(2)
      }
      a()

宿主环境

  • JS运行所在的环境,称为宿主
  • 浏览器提供了window,含有操作浏览器相关的Api 对象
  • window称为: 全局对象 或 全局作用域

作用域

  • 一类具有特殊功能的对象的称呼
  • 全局作用域:存储了系统的方法、属性–window
  • 局部作用域:函数在运行时 临时生成的对象, 用于存储函数中声明的变量们

作用域链

  • 当多层作用域嵌套时, 当使用变量时, 则遵循就近原则, 来找到对应的变量

闭包Closure

  • 闭包: 函数中使用了来自其他作用域的变量
  • 怕: 其他作用域销毁, 而导致自身无法使用其中的变量
  • 解决: 把其他作用域存储在自身的 scopes 属性里
  • 称呼: 这种被存储在scopes里的函数作用域: 叫闭包
    function a() {
      var aa = 11
      var bb = 22
      function b() {
        var bb = 33
        var cc = 44
        function c() {
          var cc = 55
          console.log(aa, bb, cc);
        }
        console.dir(c);
      }
      b()
    }
    a()

闭包应用

  • 为函数制造私有的变量,避免全局污染
  • 缺点:浪费内存
//制作一个函数displayName,每次可以调用都能打印出自身调用的次数
    var displayName = (function () {
      var i = 0
      return function () {
        i++
        console.log('调用次数:', i)
      }
    })()
    displayName()
    displayName()
    displayName()
    displayName()

arguments

  • 函数中, 隐藏自带的一个属性/变量
  • 作用: 保存函数收到的所有实参
  • 应用场景:
    • 制作实参个数不固定的函数, 例如: 求最大值
    • 函数重载: 制作多功能函数, 根据实参的个数 或 类型 不同, 内部做判断 然后执行不同的代码逻辑

函数重载

  // 任务: 制作一个计算折扣的函数
    function zhekou(money) {
      // 根据实参 来判定折扣的场景
      // console.log(arguments);
      // 如果是3个参数, 就是满减
      if (arguments.length == 3) {
        if (money >= arguments[1]) {
          money -= arguments[2]
        }
        console.log('您最终消费:', money);
      }
      if (arguments.length == 2) {
        if (typeof arguments[1] == 'number') {
          money *= arguments[1]
        }

        if (typeof arguments[1] == 'string') {
          var vip = arguments[1]
          // 语法糖: 作者提供了一些简化语法
          // if判断的{}中, 仅有一行代码, 可以省略{}
          if (vip == 'vip1') money *= 0.95
          if (vip == 'vip2') money *= 0.85
          if (vip == 'vip3') money *= 0.7
        }

        console.log('最终消费:', money);
      }
    }
    zhekou(3000, 0.7) //3000元 打7折
    zhekou(3000, 2000, 400) // 满2000 减400
    zhekou(3000, 'vip1') //打95折
    zhekou(3000, 'vip2') //打85折
    zhekou(3000, 'vip3') //打7折

this

  • 函数的this属性: 代表当前函数运行时所在的对象
    • 对象.函数() 或 对象函数名 : this就是对象
    • 函数(): this就是window

面试题

var length = 10
    function fn() {
      // this = window
      // this.length == window.length == 
      console.log(this.length);
    }
    var obj = {
      length: 5,
      method: function (fn) {
        fn() // 直接触发的函数, 其this是window   -- 10
        arguments[0]()
        // [0]: 序号0的参数,就fn.  fn是在arguments对象中, 
        // 所以: fn的this就是 arguments, 其length属性就是实参个数--2
        console.log(arguments);
      }
    }
    obj.method(fn, 1)

浅拷贝

  • 浅拷贝: 把对象复制一份 而不是简单的 地址传递
var emp1 = {
      ename: "mike",
      age: 19,
      phone: '18989898988'
    }

    // 1. 制作一个新的空对象
    var emp2 = {}
    // 2. 遍历 emp1 , 把其中的属性挨个复制到空对象里
    for (key in emp1) {
      console.log('key:', key);
      //通过属性名读值
      var value = emp1[key]
      // 相同的 属性名和值, 存入到 emp2 对象里
      emp2[key] = value
    }
    // 修改emp1的属性, 不会影响到emp2, 因为他们是不同的对象
    emp1.age = 100

    console.log(emp2);

属性访问

 var obj = { gege: '格格', xuan: '子轩' }
 
    var gege = 'xuan'
    
    console.log(obj.gege) // 点语法, .后是属性名   -- '格格'
    
    // 方括号, 值是JS代码. gege是个变量, 其值是'xuan'
    // 实际读取的是 obj['xuan']   - 子轩
    console.log(obj[gege]) 

构造函数

  • 用于构造创建对象的函数
// 构造函数: 用于 构造对象 的函数

    // 矩形对象: 

    // 命名规范: 用 大驼峰写法 给构造函数命名
    // 目的: 让使用者 见名知意
    function Rect(length, width) {
      // 准备个空对象, 把值放进去, 返回对象
      var obj = {}

      obj.length = length
      obj.width = width

      return obj
    }

    // 调用构造函数, 传入值. 会返回一个对象
    var r1 = Rect(10, 5)
    console.log(r1);

    var r2 = Rect(100, 30)
    console.log(r2)

原型

  • 一个共享的存储区域, 专门存放对象共享的方法
  • __proto__原型链用于 链接到原型的属性

对象中的属性

  • 独有的:不同对象有不同属性值,分别存在不同的对象里面
  • 共享的:函数,可以按照固定的逻辑处理对象的属性
    • 为了节省内存:把共享的函数存储在 构造函数的prototype的属性里
    • 生成对象时,要通过其__proto__属性关联到共享的prototype属性
    • prototype:原型–构造函数,存共享方法
  • 使用时
    • 对象.方法(): 对象会到其 __proto__ 链接到的 prototype 中查找方法并使用
  • new运算符:简化构造函数的代码, 省3
    • var this = {} //有的面试答案: 这是两步 声明空对象, 然后赋值给this
    • this.__proto__ = 构造函数.prototype
    • return this

严格模式

ES5 提供了更多的报错, 辅助程序员写出更健康的代码use stirct

对象属性精确配置

  • Object : 是对象类型的构造函数, 其中还包含很多操作对象类型的方法
  • defineProperty
  • 参数1: 要配置的对象
    参数2: 要配置的属性名
    参数3: 具体的配置项
    Object.defineProperty(emp, 'eid', {
      // ctrl+i: 弹出提示
      writable: false  // 是否可写入新的值,  true可以  false不可以
    })
  • writable: false // 是否可写入新的值, true可以 false不可以
  • enumerable: false //是否遍历 true可 false不可
  • defineProperties此方法可以一次配置多个属性
    Object.defineProperties(emp, {
      // 属性名: 配置项
      salary: { enumerable: false },
      // configurable: 是否可重新配置  true可  false不可
      eid: { writable: false, configurable: false } //w回车 : f 回车
    })

计算属性

  • get:属性是函数类型,使用时不需要()触发
  • set:可以监听的赋值属性,对值进行判定是否合法
    • 合法:准备一个额外的属性,通常名称_xxx用于存储合法的值
      • 读取时:搭配get计算属性,来从_xxx读值
    • 不合法:抛出错误
    var emp = { ename: "凯凯" }

    // 细节: _salary 是为了辅助监听器而生, 属于幕后, 不应该被遍历出来
    // 用 define方式新增的属性, 默认是 不可遍历, 不可配置, 不可赋值
    Object.defineProperty(emp, '_salary', { writable: true })

    // 任务: 添加 salary属性监听器...
    Object.defineProperty(emp, 'salary', {
      enumerable: true, //可以被遍历到
      get() { return this._salary },
      set(value) {
        if (value >= 5000 && value <= 50000) {
          this._salary = value
        } else {
          throw Error('薪资错误:' + value)
        }
      },
    })
    // 设置薪资: 范围 5000~50000 
    // emp.salary = 1200 //报错: 薪资错误

    emp.salary = 24000 //正常赋值
    console.log(emp.salary) //打印出 24000
    console.log(emp);

保护对象属性

  • 不能删:preventExtensions
  • 不能增删:seal
  • 不能增删改:freeze

箭头函数

匿名化了匿名函数的书写,带有两个语法糖

  • 参数只有1个,省略形参的()
  • 函数体只有一行,省略return 和 {}
    • 如果返回值是对象类型,{}用()包围,防止歧义
  • 箭头函数的this指向
    • 箭头函数自身没有this属性
    • 根据作用域的就近原则,使用上级作用域中的this

数组高阶函数

every:判断数组每一个元素都符合条件–类似逻辑与
some:判断数组中有至少一个元素符合条件–类似逻辑或
filter:把满足条件的元素组合成新的数组
map:映射-把数组的元素 按照固定的规范,返回值组合成新的数组

<body>
  <table>
    <thead>
      <tr>
        <th>序号</th>
        <th>名称</th>
        <th>单价</th>
        <th>数量</th>
        <th>总价格</th>
      </tr>
    </thead>
    <tbody id="box">
      <tr>
        <td>1</td>
        <td>香蕉</td>
        <td>¥9</td>
        <td>4</td>
        <td>¥36</td>
      </tr>
    </tbody>
  </table>

  <script>
    var products = [
      { pname: "香蕉", price: 9, count: 4 },
      { pname: "葡萄", price: 25, count: 14 },
      { pname: "西瓜", price: 40, count: 6 },
      { pname: "桃子", price: 12, count: 8 },
      { pname: "杨梅", price: 29, count: 11 },
    ]

    // 1. 是否所有单价都超过10元
    var p = products.every(v => v.price > 10)
    console.log(p ? '都超过10元' : '非都超过10元');
    // 2. 是否有数量少于5个的
    var p = products.some(v => v.count < 5)
    console.log(p ? '有数量少于5个' : '没有数量少于5个');

    // 3. 找出总价格高于100元的产品
    var a = products.filter(v => v.price * v.count > 100)
    console.log(a)

    // 4. 商品显示在表格里
    var a = products.map((v, index) => `<tr>
        <td>${index + 1}</td>
        <td>${v.pname}</td>
        <td>¥${v.price}</td>
        <td>${v.count}</td>
        <td>¥${v.price * v.count}</td>
      </tr>`)

    box.innerHTML = a.join('')
  </script>
</body>
  • forEach:遍历

    • 遍历数组的方案
      • for 普通循环
      • for…in 遍历对象类型
      • for…of 专为数组而生,直接遍历值
  • reduce 数组合并

    • 把每次累加后的值,传递给下一次循环的函数
      var nums = [1, 32, 54, 645, 2, 43, 46, 576];
      var a = nums.reduce((sum, value, index) => {
        return sum + value;
      }, 0);
      console.log(a);

let和const

  • 没有全局污染,声明的变量存储在脚本作用域
  • let可变 const不可变
  • 关于声明提升,有提升 但是 存在暂存死区

块级作用域

{}配合let/const快速形成块级作用域–代替闭包制作私有变量

展开语法

…数组
…对象

//灵活应用场景 互换的变量的值
      var a = 20;
      var b = 40;
      [a, b] = [b, a];
      console.log(a, b);
 var gname = {
        gname: "LOL",
        maker: "腾讯",
        teams: ["web", "edg", "rng", "v5"],
        desc: {
          year: 2018,
        },
      };
      var {
        gname,
        maker,
        teams: [t1, t2, t3, t4],
        desc: { year },
      } = gname;
      console.log(gname, maker, t1, t2, t3, t4, year);

解构语法

  • 数组:const [变量,变量] = 数组
  • 对象:const{变量,属性名:别名}=对象
  • 函数的形参可以直接解构
      var webs = [
        { title: "新闻", href: "http://tmooc.cn" },
        { title: "hao123", href: "https://www.douyu.com/" },
        { title: "地图", href: "https://developer.mozilla.org/zh-CN/" },
        { title: "贴吧", href: "https://www.jd.com/" },
        { title: "视频", href: "https://www.jd.com/" },
        { title: "图片", href: "https://www.jd.com/" },
      ];
      var a = webs.map(
        ({ title, href }) => `
      <a href="${href}">${title}</a>
      `
      );
      box.innerHTML += a.join("");

函数增强语法

函数参数默认值:function(参数=值)

      function show(name = "亮亮") {
        console.log("name", name);
      }
      show();
      show("泡泡");

剩余参数 :function(…args){}

      function bb(x, y, ...args) {
        console.log(x, y, args);
      }
      bb(11, 22, 32, 3, 4, 5, 6, 7, 66, 55);

函数的方法

函数的触发方式

用(),apply,bind,call

作用与区别

  • call-调用函数时, 临时设置其this指向, 运行完毕会从对象里删除
  • bind-返回一个新的函数, 把this指向和参数绑定在其上方, 延时触发
  • apply-立刻触发函数, 区别是 参数用 数组来传递
//call
var r1 = { width: 10, height: 30 }
    var r2 = { width: 55, height: 60 }
    var r3 = { width: 13, height: 10 }

    // 制作一个函数, 分别计算出他们的面积
    // 把函数传入对象里, 执行
    function area() {
      console.log(this.width * this.height);
    }

    // area函数, 临时放到 r1 里执行. 函数中的this就是所在的对象
    area.call(r1)
//apply
 var r1 = { x: 10, y: 20 }

    function bb(z, k) {
      console.log(this.x + this.y + z + k)
    }

    // 普通方案: 把bb临时放到r1里执行
    r1.bb = bb
    r1.bb(30, 40) // bb函数前方的对象就是其this指向
    delete r1.bb

    // 用call方法实现:  函数.call(要放到的对象, 其他参数...)
    bb.call(r1, 30, 40)

    // apply: 与call相似, 但是区别是参数要放数组里
    bb.apply(r1, [30, 40])

    console.log(r1)
//bind
    // bind: 用于绑定this指向, 延迟触发函数
    function a(x, y) {
      console.log(this, x, y);
    }

    var emp = { ename: "泡泡" }

    // bind: 其返回值是一个函数, 其内存储了this指向和其余参数
    var a_bind = a.bind(emp, 10, 20)
    console.dir(a_bind);

    // 2s后, 触发 a_bind
    setTimeout(a_bind, 2000);

class语法

  var emp = {
      ename: "泡泡",
      age: 18,
      phone: '18898989898'
    }
    console.log(emp)
    console.log(emp.ename)

构造函数(JAVA)

class Rect1 {
      // java的类中, 不需要写 function 关键词
      constructor(width, height) {
        this.height = height
        this.width = width
      }

      area() {
        return this.width * this.height
      }

      zc() {
        return (this.width + this.height) * 2
      }
    }

    console.dir(Rect1) // 查看其prototype

    var r2 = new Rect1(100, 50)
    console.log(r2)
    console.log(r2.area());
    console.log(r2.zc());

继承

面向对象有三大特征: 封装 继承 多态

  1. 封装: 用{}把代码封在一起, 起个名字. 以后通过名字来调用这段代码
  2. 继承: 自己没有的 到父中查找… JS的原型链 proto
  3. 多态: 重写理论
class Father {
      money = '500w'

      eat() {
        console.log('吃饭');
      }
    }
 class Son extends Father {
      phone = '19898989838'
      play() {
        console.log('玩游戏');
      }
    }
  var s = new Son()
  console.log(s) //查看其原型 __proto__

多态

多态: 一个父类 可以拥有不同的子类. 由于子类中可以重写, 所以不同的子类调用相同的方法或属性 可以出现不同的状态

    // 多态: 一个父类 可以拥有不同的子类. 由于子类中可以重写, 所以不同的子类调用相同的方法或属性 可以出现不同的状态
    class Father {
      hair = "黑头发"
      eye = '黑眼睛'

      eat() {
        console.log('吃粗粮');
      }
    }
    // 子类 继承 父类, 拥有父类的所有属性和方法
    class Son extends Father {
      // 重写override: 子类中可以书写与父类中 属性同名的, 会覆盖
      hair = '金发'

      // 重写与父类同名的方法, 当使用eat方法时, 根据原型链的就近原则, 优先使用自身原型的eat方法
      eat() {
        // 强行使用父元素的eat, 通过关键词super
        super.eat()

        console.log('吃外卖');
      }
    }

    var s1 = new Son()
    console.log(s1)
    console.log(s1.hair, s1.eye);

回调地狱

// JS的异步操作 通常利用回调函数来实现 -- 网络请求
// 当多个异步操作需要同步执行时, 就会出现回调地狱问题
// 期望:点击注册按钮时, 要有序的验证: 用户名是否重复->邮箱..->手机号->注册

Promise

// ES6提供了 构造函数Promise, 可以从语法上规避回调地狱问题

    // 学习Promise, 必须背 其固定格式
    // resolve:解决    reject:拒绝   then:然后  catch:抓取

    // Promise有三种状态
    // 1. pending: 待定. new Promise之后处于的状态
    // 2. fulfilled: 已兑现. 调用resolve后的状态, 代表成功
    // 3. rejected: 已拒绝. 调用reject后的状态, 代表失败

    new Promise((resolve, reject) => {
      // 设定: 状态只能从pending 切换成其他状态

      // resolve: 会触发then中的函数, 其参数传递给then中的函数
      // resolve({ msg: '成功', code: 200 }) //进入 fulfilled状态

      // resolve(1111) //无法触发, 因为当前是fulfilled状态

      // 拒绝: 代表失败. 会触发catch中的函数, 参数会传给catch的函数, 进入rejected 状态
      reject(222222)
    })
      .then(res => {
        console.log('res:', res);
      })
      .catch(err => {
        console.log('err:', err);
      })
  <script>
    // 改造1: 用变量存储 new出来的promise对象; 再用p调用then和catch
    function checkUname() {
      return new Promise((resolve, reject) => {
        console.log('验证用户名...');
        setTimeout(() => {
          const n = Math.random() //获取随机数,范围 0~1
          if (n > 0.3) {
            // resolve代表成功, 触发then
            resolve({ msg: "成功", n })
          } else {
            // reject代表失败, 触发catch
            reject({ msg: "失败", n })
          }
        }, 1000);
      })

      // return p
    }

    // 检查邮箱
    function checkEmail() {
      // prom : 前提安装 ES6提示插件, 详见B站 vscode视频
      return new Promise((resolve, reject) => {
        console.log('验证邮箱...');
        setTimeout(() => {
          const n = Math.random()
          if (n > 0.3) {
            resolve({ msg: "邮箱验证成功", n })
          } else {
            reject({ msg: "邮箱验证失败", n })
          }
        }, 1000);
      });
    }

    function checkPhone() {
      return new Promise((resolve, reject) => {
        console.log('验证手机号...');
        setTimeout(() => {
          const n = Math.random()
          if (n > 0.3) {
            resolve({ msg: "手机号正确!", n })
          } else {
            reject({ msg: "手机号错误!", n })
          }
        }, 1000);
      });
    }

    function register() {
      return new Promise((resolve, reject) => {
        console.log('开始注册!');
        setTimeout(() => {
          const n = Math.random()
          if (n > 0.3) {
            resolve({ msg: "注册成功", n })
          } else {
            reject({ msg: "注册失败", n })
          }
        }, 1000);
      });
    }

    // 通过调用函数, 返回值是 new Promise()
    // then: 然后
    checkUname().then(res => {
      console.log('res:', res);
      // 返回值会触发下一个 then
      return checkEmail()
    }).then(res => {
      console.log('res:', res)
      return checkPhone()
    }).then(res => {
      console.log('res:', res)
      return register()
    }).then(res => {
      console.log('res:', res)
    }).catch(err => {
      console.log('err:', err)
    })
  </script>

  <script>
    var a = 10
    // console.log(a * 2)

    function b() {
      // var a = 10
      return 10
    }
    // console.log(b() * 2)

  </script>

正则表达式

正则匹配

    // 正则特殊字符: \d 代表1个数字, 等价于 [0-9]

    var words = '亮亮666 泡泡789 凯凯123'

    // 要求: 找出words中, 所有的数字
    // 字符串提供了 match 方法, 可以找出符合正则表达式要求的字符
    console.log(String.prototype);

    // 正则的特殊字符要放在 // 中, JS才能识别
    var a = words.match(/\d/)  //match: 匹配单个
    console.log(a)

正则替换

  var phone = prompt("请输入手机号")
    console.log('phone:', phone)

    // 替换语法:  字符串.replace(正则, 替换成什么)

    // 需求1: 把所有的数字改成*
    var a = phone.replace(/\d/g, '*')
    console.log('a:', a)

正则验证

  var phone = prompt('请输入手机号')
    console.log('phone:', phone)

    // 手机号的规则: 
    // 1开头; 第二位 3-9; 共11个
    // ^: 代表字符串的开头
    // $: 代表字符串的结尾
    var r = /^1[3-9]\d{9}$/

    // 验证语法: 正则.test(字符串)   返回值是true和false 代表对还是不对
    var a = r.test(phone)
    console.log(a ? '正确' : '格式错误');

构造方式

 // 正则的字面量写法: 简单
    var a = /\d/
    console.dir(a)

    // 构造写法
    // 参数1: 正则   参数2: 修饰符
    // \ : 在JS字符串中转义符.   例如 \d 会转成 d
    // \\: 把\转义成普通\   \\d -> \d
    var b = new RegExp('\\d', 'g')

    var words = 'abcd 1234'

    console.log(words.match(b))
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值