美文网首页
js with-eval函数-严格模式-面向对象 操作属性的控制

js with-eval函数-严格模式-面向对象 操作属性的控制

作者: 咸鱼不咸_123 | 来源:发表于2022-04-28 19:40 被阅读0次

    1.with语句

    扩展一个语句的作用域链

    mdn强调:不建议使用with语句,它可能是混淆错误和兼容性问题的根源

    • with会形成自己的作用域
    • 目前已经不推荐使用了
    • with在严格模式下是不能使用的
    var message="Hello Global";
    
    
    //with语句:可以形成自己的作用域
    var obj={name:"wjy",age:20,message:"obj"}
    function foo(){
      
      function bar(){
        with(obj){ //先查找 with传入的对象中查找,再去查找对应的AO对象,如果还是没找到会一直往上查找,直到全局作用域
          console.log(message);
        }
        
      }
      bar()
    }
    
    /**
     * * es5的作用域有两种 函数作用域 和全局作用域
     */
    foo()
    

    2.eval函数

    eval是一个特殊的函数,它可以将传入的字符串当做javascript代码来运行。

    var jsString='var message="Hello World";console.log(message);'
    
    eval(jsString)
    

    不建议在开发中使用

    • eval代码的可读性非常的差(代码的可读性是高质量代码的重要原则)
    • eval是一个字符串,那么有可能在执行的过程中被刻意篡改,那么可能会造成被攻击的风险
    • eval的执行必须经过JS解释器,不能被JS引擎优化
      • 会被JS解释器解释为bytecode再转化机器码进行运行

    2.1 应用场景

    webpack在进行项目打包的时候,会将es6和ts的代码转化为es5的一些语法。

    其实可以在webpack的devtool设置为eval

    • 它会将js代码转化为字符串
    • 性能会更高

    3.严格模式

    在ECMAScript5标准(es5)中,JavaScript提出了严格模式的概念(Strict Mode)

    • 严格模式很好理解,是一种具有限制性的JavaScript模式,从而使代码隐式脱离了“懒散模式”,
    • 支持严格模式的浏览器在检测到代码中有严格模式时,会以更加严格的方式对代码进行检测和执行

    严格模式对正常的JavaScript语义进行了一些限制:

    • 严格模式通过抛出错误 来消除 原有的 静默(silent) 错误

      • l23.name="abc";//静默错误
        
    • 严格模式下让JS引擎在执行代码时可以进行更多的优化(不需要对一些特殊的语法进行处理)

    • 严格模式禁用了在ECMAScript未来版本中可能会定义的一些语法(不能使用保留字作为标识符了)

    保留字 是未来可能会变为关键字

    3.1 开启严格模式

    那么如何开启严格模式呢?严格模式支持粒度话的转移

    • 可以支持在js文件中开启严格模式(文件头部加 "use strict")
    • 也支持对某个函数开启严格模式 (函数执行体头部 加 "use strict")
    "use strict"
    var message="Hello World"
    console.log(message);
    // * 静默错误
    true.foo="abc"
    

    在实际项目开发中,js文件并不是直接部署到服务器上的,而是先让打包工具(webpack、vite、rollup)先打包,在打包的过程中会自动注入严格模式

    function foo(){
      "use strict"
      false.bar="bar"
    }
    
    foo()
    

    3.2 严格模式限制

    这里我们来说几个严格模式下的严格语法限制:

    • JavaScript被设计为新手开发者更容易上手,所以有时候本来错误语法,被认为也是可以正常被解析的
    • 但是这种方式可能会带来安全隐患
    • 在严格模式下,这种失误就会被当做错误,以便可以快速的发现和修改
    1. 无法意外的创建全局变量

    2. 严格模式会引起静默失败(silently fail,注:不报错也没有任何效果)的赋值操作抛出异常

    3. 严格模式下试图删除不可删除的属性

    4. 严格模式不允许函数参数有相同的名称

    5. 不允许0的八进制语法

    6. 在严格模式下,不能使用with

    7. 在严格模式下,eval不再为上层引用变量

    8. 严格模式下,this绑定不会默认转成对象

    3.2.1 无法意外的创建全局变量
    "use strict"
    // *1. 不能意外的创建全局变量
    
    message="hello";
    function foo(){
      age=20;
    }
    
    3.2.2 不允许函数有相同的参数名
    "use strict"
    // * 2.不允许函数有相同的参数名称
    function foo(x,y,x){
      console.log(x,y,x);
    }
    foo(1,2,3)
    
    3.2.3 引起静默失败
    "use strict"
    // * 3 引起静默失败
    NaN=123
    false.name="wjy"
    
    var obj={};
    Object.defineProperty(obj,"name",{
      writable:false,
      configurable:false
      value:"wjy"
    })
    obj.name='coderwhy'
    delete obj.name
    
    • writable:是否可写
    • configurable;是否可配置
    • value:设置值
    3.2.4 不允许0的八进制
    "use strict"
    // * 不允许0的八进制语法
    // * 不允许0的八进制语法
    // var num=0123; //在严格模式下是错误的
    var num2=0o123 ;//es6中使用 0o开头表示八进制
    var num3=0x123;//es6 0x表示 十六进制
    var num4=0b101 //es6 0b表示二进制
    console.log(num2,num3,num4); //83 291 5
    
    3.2.5 eval不会为上层引用变量
    // * eval 不会为上层引用变量
    var jsString='var message="Hello World";console.log(message);'
    eval(jsString)
    console.log(message); //非严格模式下,eval会为全局作用域添加message属性,所以是可以打印出来的
    
    "use strict"
    // * eval 不会为上层引用变量
    var jsString='var message="Hello World";console.log(message);'
    eval(jsString)
    console.log(message); //非严格模式下,eval会为全局作用域添加message属性,所以是可以打印出来的
    
    3.2.6 with语句不允许使用
    3.2.7 this不会默认转化为对象
    • 自执行函数的this指向undefined
    "use strict"
    function foo(){
      console.log(this);
    }
    foo()  //undefined
    
    var obj={
      name:"wjy",
      foo:foo
    }
    obj.foo();//obj
    
    //源码上其实是window.setTimeout(fn,delay) 调用的使用是fn.apply(this,...)
    setTimeout(()=>{
      console.log(this);//window
    })
    

    4.面向对象

    4.1 面向对象是现实的抽象方式

    • 对象是JavaSxript中一个非常重要的概念,这是因为对象可以将多个相关联的数据封装到一起,更好的描述一个事物

      • 比如我们可以描述一辆车:Car,具有颜色(color)、速度(speed)、品牌(brand)、价格(price)、行驶(travel)等等
      • 比如我们可以描述一个人:Person,具有姓名(name)、年龄(age)、身高(height)、吃东西(eat)、跑步(run)等等
    • 对象来描述事物,更有利于我们将现实的事物,抽离成代码中的某个数据结构

      • 所以有一些编程语言就是纯面向对象的编程语言,比如:Java
      • 你在实现任何现实抽象时都需要先创建一个类,根据类再去创建对象

    4.2 JavaScript的面向对象

    JavaScript其实支持多种编程范式,包括 函数式编程面向对象编程

    • javascript的对象被设计为一组属性的无序集合,像是一个哈希表,有key和value组成
    • key是一个标识符名称value可以是任意类型,可以是其他对象或函数
    • 如果值是一个函数,那么我们可以称之为是对象的方法
    4.2.1 如何创建一个对象
    • 早期使用创建对象的方式最多的是 使用Object类,并且使用 new 关键字来创建一个对象
      • 这是因为早期很多javascript开发者是从java过来的,它们也更习惯于java中通过new的方式来创建一个对象。
    • 后来很多开发者为了方便起见,都是直接通过字面量的形式来创建对象
      • 这种形式看起来更加的简洁,并且对象和属性之间的内聚性也更强,所以这种方式后来就流行了起来。
    // 创建一个对象,对某一个进行抽象(描述)
    //*  1.创建 方式一:通过new Object()创建
    var obj=new Object();
    obj.name="wjy"
    obj.age=18;
    obj.height=160
    obj.running=function(){
      console.log(this.name+"在跑步 ");
    }
    
    // 2.创建方式二:字面量形式
    var info={
      name:'kobo',
      age:50,
      height:190,
      eating:function(){
        console.log(this.name+"在吃东西");
      }
    }
    
    4.2.2 对象属性的操作
    • 获取属性
      • 对象.属性名
      • 对象.['属性名']
    • 对属性赋值
      • 对象.属性名=xxxx
    • 删除属性
      • delete 对象.属性名
    • 对象属性的遍历
      • for ……in
    var obj={name:"wjy",age:20}
    // 获取属性
    console.log(obj.name);
    
    // 对属性进行赋值
    obj.name="hyz"
    
    // 遍历属性
    for(let key in obj){
      console.log(key);
    }
    // 删除属性
    delete obj.name
    
    4.2.3对属性操作的控制
    • 在前面我们的属性都是直接定义在对象内部,或者直接添加到对象内部

      • 但是这样来做的时候就不能对这个属性进行一些限制:比如这个属性是否是可以通过delete删除的?这个属性是否在for..in遍历的时候背遍历出来
    • 如果我们想要对一个属性进行比较精准的操作控制,那么我们就可以使用属性描述符

      • 通过属性描述符可以精确的添加或修改对象的属性
      • 属性描述符需要使用Object.defineProperty来对属性进行添加或修改
    4.2.4 Object.defineProperty(会修改原有对象,不是纯函数)
    • Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改对象的现有属性,并返回此对象

      • Object.defineProperty(obj,prop,descriptor)
        
    • 可接收三个参数:

      • obj要定义属性的对象
      • prop要定义或修改的属性的名称或Symbol
      • descriptor要定义或修改的属性描述符
        • 属性描述符是一个对象
    • 返回值:

      • 被传递给函数的对象
    var obj={
      name:"wjy",
      age:18
    }
    // * 属性描述符是一个对象,这个对象有很多的配置
    /**
     * * value:设置属性的值
     */
    Object.defineProperty(obj,"height",{
      value:1.88,//* 默认添加的属性是不可枚举的,不可遍历的
    })
    console.log(obj); //* 直接打印是没有height属性的  { name: 'wjy', age: 18 }
    console.log(obj.height); //* 直接访问height可以取到值 1.88
    
    4.2.5 属性描述符的分类

    属性描述符的类型有两种:

    • 数据属性(Data Properties)描述符(descriptor)

    • 存取属性(Accessor访问 器 Properties)描述符(Descriptor)

      configurable enumerable value writable get set
      数据描述符 可以 可以 可以 可以 不可以 不可以
      存取描述符 可以 可以 不可以 不可以 可以 可以
    4.2.5.1 数据属性描述符

    数据属性描述符有如下4个特性:

    • [[Configurable]]:表示属性是否可以通过delete删除属性,是否可以修改它的特性、或者是否可以将它修改为存取属性描述符

      • 当我们直接在一个对象上定义某个属性时,这个属性的[[Configurable]]为true
      • 当我们通过属性描述符定义一个属性时,这个属性的[[Configurable]]默认为false
    • [[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()来返回该属性

      • 当我们直接在一个对象上定义一个属性时,这个属性的[[Enumerable]]为true
      • 当我们通过属性描述符定义一个属性时,这个属性的[[Enumerable]]默认为false
    • [[Writable]]:表示是否可以修改属性的值

      • 当我们直接在一个对象上定义一个属性时,这个属性的[[Writable]]为true
      • 当我们通过属性描述符定义一个属性时,这个属性的[[Writable]]默认为false
    • [[value]]:属性的value值,读取属性值时会返回该值,修改属性时,会对其进行修改

      • 默认情况下这个值是undefined

    configurable

    var obj={
      name:'wjy',
      age:22
    }
    
    Object.defineProperty(obj,"height",{
      value:1.99,
      configurable:false,//如果为false,则不能删除该属性,也不能重新定义属性描述符
    })
    delete obj.height
    console.log(obj.height); //* 属性并没有删除 
    
    Object.defineProperty(obj,"height",{  //* 会报错
      value:2.22,
      configurable:true
    })
    

    enumerable

    var obj={
      name:'wjy',
      age:22
    }
    
    Object.defineProperty(obj,"height",{
      value:1.99,
      configurable:true,//如果为false,则不能删除该属性,也不能重新定义属性描述符
      enumerable:false
    })
    
    for(let key in obj){
      console.log(key);
    }
    console.log(Object.keys(obj));
    

    writable

    var obj={
      name:'wjy',
      age:22
    }
    
    Object.defineProperty(obj,"height",{
      value:1.99,
      configurable:true,//如果为false,则不能删除该属性,也不能重新定义属性描述符
      enumerable:false,
      writable:false,//* 属性的值是否可以被修改
    })
    
    obj.height=2.22 //* 这个属于静默错误
    console.log(obj.height);//* 属性的值没有被修改成功,
    
    • 直接在对象内部定义属性或者对象上定义属性

      • 默认的value为赋值的值
      • 默认的configurable为true
      • 默认的enumerable为true
      • 默认的writable为true
    • 如果是通过属性描述符定义属性

      • 默认的value为undefined
      • 默认的configurable为false
      • 默认的enumerable为false
      • 默认的writable为false
    4.2.5.1 存取属性描述符

    存取属性描述符有如下的4个特征

    • [[configurable]]:表示属性是否可以用delete删除,是否可以修改新特性,是否可以转换为数据属性描述符

      • 和数据属性描述符一致
      • 当我们直接在一个对象上定义某个属性时,,这个属性的[[configuralbe]]为true
      • 当我们通过属性描述符定义的属性,这个属性的[[configurable]]默认为false
    • [[enumerable]];表示这个属性是否可以通过for……in或Object.keys()返回的属性

      • 和数据属性描述符一致
      • 如果直接在对象上定义某个属性,则这个属性的[[enumerable]]为true
      • 通过属性描述符定义的属性,这个属性的[[enumerable]]默认为false
    • [[get]]:获取属性时会执行的函数。默认为undefined

    • [[set]]:设置属性时会执行的函数。默认为undefined

    应用场景

    • 隐藏某个属性被外界直接使用和设置
    • 截取对象的某个属性在访问和设置的过程,可以通过存取属性描述符
    var obj={
      name:"wjy",
      age:20,
      _address:"怀化市"
    }
    
    //* 隐藏某个私有属性被外界直接使用和赋值
    //* 如果我们希望能获取某个属性它访问和设置值的过程时,也会使用存取属性描述符
    Object.defineProperty(obj,"address",{
      configurable:true,
      enumerable:true,
      get(value){
        foo()
        return this._address;
      },
      set(value){
        bar()
        this._address=value
      }
    })
    
    console.log(obj.address);
    obj.address="北京市"
    console.log(obj.address);
    
    function foo()
    {
      console.log("获取了address的值");
    }
    function bar(){
      console.log("设置了address的值");
    }
    

    5.总结

    with-eval-严格模式-面向对象.png

    相关文章

      网友评论

          本文标题:js with-eval函数-严格模式-面向对象 操作属性的控制

          本文链接:https://www.haomeiwen.com/subject/wvwdyrtx.html