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

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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容