2021年最新 vue面试题整理

thbcm阅读(169)

一、对MVVM的理解

MVVM分为Model、View、ViewModel。

Model 代表数据模型,数据和业务逻辑都在Model层中定义;泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的 api 接口。

View 代表UI视图,负责数据的展示;视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建 。

ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;

Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步。+

这种模式实现了 Model 和 View 的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作 dom。

ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。

MVVM 框架实现了双向绑定,这样 ViewModel 的内容会实时展现在 View 层,前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新。这样 View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

二、vue常见指令

1. v-text

v-text 主要用来更新 textContent,可以等同于 JS 的 text 属性。

<span v-text="msg"></span>

这两者等价:

<span>插值表达式{{msg}}</span>

2. v-html

双大括号的方式会将数据解释为纯文本,而非 HTML。为了输出真正的 HTML,可以用 v-html 指令。它等同于 JS 的 innerHtml 属性。

<div v-html="rawHtml"></div>

这个div的内容将会替换成属性值 rawHtml,直接作为 HTML 进行渲染。

3. v-pre

v-pre 主要用来跳过这个元素和它的子元素编译过程。可以用来显示原始的 Mustache 标签。跳过大量没有指令的节点加快编译。

<div id="app">
    <span v-pre>{{message}}</span>  //这条语句不进行编译
    <span>{{message}}</span>
</div>

最终仅显示第二个 span 的内容

4. v-cloak

这个指令是用来保持在元素上直到关联实例结束时进行编译。

<div id="app" v-cloak>
    <div>
        {{message}}
    </div>
</div>
<script type="text/javascript">
    new Vue({
      el:'#app',
      data:{
        message:'hello world'
      }
    })
</script>

在页面加载时会闪烁(插值闪烁问题),先显示:

<div>
    {{message}}
</div>

然后才会编译为:

<div>
    hello world!
</div>

可以用 v-cloak 指令解决插值表达式闪烁问题,v-cloak 在 css 中用属性选择器设置为 display: none;

5. v-once

v-once 关联的实例,只会渲染一次。之后的重新渲染,实例极其所有的子节点将被视为静态内容跳过,这可以用于优化更新性能。

<span v-once>This will never change:{{msg}}</span>  //单个元素
<div v-once>//有子元素
    <h1>comment</h1>
    <p>{{msg}}</p>
</div>
<my-component v-once:comment="msg"></my-component>  //组件
<ul>
    <li v-for="i in list">{{i}}</li>
</ul>

上面的例子中,msg,list 即使产生改变,也不会重新渲染。

6. v-if

v-if 可以实现条件渲染,Vue 会根据表达式的值的真假条件来渲染元素。

<a v-if="ok">yes</a>

如果属性值 ok 为 true,则显示。否则,不会渲染这个元素。

7. v-else

v-else 是搭配 v-if 使用的,它必须紧跟在 v-if 或者 v-else-if 后面,否则不起作用。

<a v-if="ok">yes</a>
<a v-else>No</a>

8. v-else-if
v-else-if 充当 v-if 的 else-if 块,可以链式的使用多次。可以更加方便的实现 switch 语句。

<div v-if="type==='A'">
    A
</div>
<div v-else-if="type==='B'">
    B
</div>
<div v-else-if="type==='C'">
    C
</div>
<div v-else>
    Not A,B,C
</div>

9. v-show

<h1 v-show="ok">hello world</h1>

也是用于根据条件展示元素。和 v-if 不同的是,如果 v-if 的值是 false,则这个元素被销毁,不在 dom 中。但是 v-show 的元素会始终被渲染并保存在 dom 中,它只是简单的切换 css 的 dispaly 属性。

注意:v-if 有更高的切换开销 v-show 有更高的初始渲染开销。因此,如果要非常频繁的切换,则使用 v-show 较好;如果在运行时条件不太可能改变,则 v-if 较好

10. v-for
用 v-for 指令根据遍历数组来进行渲染
有下面两种遍历形式

<div v-for="(item,index) in items"></div>   //使用in,index是一个可选参数,表示当前项的索引
<div v-for="item of items"></div>   //使用of

下面是一个例子,并且在 v-for 中,拥有对父作用域属性的完全访问权限。

<ul id="app">
    <li v-for="item in items">
        {{parent}}-{{item.text}}
    </li>
</ul>
<script type="text/javascript">
    var example = new Vue({
      el:'#app',
      data:{
        parent:'父作用域'
        items:[
          {text:'文本1'},
          {text:'文本2'}
        ]
      }
    })
</script>

会被渲染为:

<ul id="app">
    <li>父作用域-文本1</li>
    <li>父作用域-文本2</li>
</ul>

注意:当 v-for 和 v-if 同处于一个节点时,v-for 的优先级比 v-if 更高。这意味着 v-if 将运行在每个 v-for 循环中

11. v-bind

v-bind 用来动态的绑定一个或者多个特性。没有参数时,可以绑定到一个包含键值对的对象。常用于动态绑定 class 和 style。以及 href 等。简写为一个冒号【 :】

<1>对象语法:

//进行类切换的例子
<div id="app">
    <!--当data里面定义的isActive等于true时,is-active这个类才会被添加起作用-->
    <!--当data里面定义的hasError等于true时,text-danger这个类才会被添加起作用-->
    <div :class="{'is-active':isActive, 'text-danger':hasError}"></div>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            isActive: true,  
            hasError: false
        }
    })
</script>

渲染结果:

<!--因为hasError: false,所以text-danger不被渲染-->
<div class = "is-active"></div>

<2>数组语法

<div id="app">
    <!--数组语法:errorClass在data对应的类一定会添加-->
    <!--is-active是对象语法,根据activeClass对应的取值决定是否添加-->
    <p :class="[{'is-active':activeClass},errorClass]">12345</p>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            activeClass: false,
            errorClass: 'text-danger'
        }
    })
</script>

渲染结果:

<!--因为activeClass: false,所以is-active不被渲染-->
<p class = "text-danger"></p>

<3>直接绑定数据对象

<div id="app">
    <!--在vue实例的data中定义了classObject对象,这个对象里面是所有类名及其真值-->
    <!--当里面的类的值是true时会被渲染-->
    <div :class="classObject">12345</div>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            classObject:{
                'is-active': false,
                'text-danger':true
            }           
        }
    })
</script>

渲染结果:

<!--因为'is-active': false,所以is-active不被渲染-->
<div class = "text-danger"></div>

12. v-model
这个指令用于在表单上创建双向数据绑定
v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值。因为它选择 Vue 实例数据做为具体的值。

<div id="app">
    <input v-model="somebody">
    <p>hello {{somebody}}</p>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            somebody:'小明'
        }
    })
</script>

这个例子中直接在浏览器 input 中输入别的名字,下面的 p 的内容会直接跟着变。这就是双向数据绑定。

v-model 修饰符<1> .lazy 默认情况下,v-model 同步输入框的值和数据。可以通过这个修饰符,转变为在 change 事件再同步。

<input v-model.lazy="msg">

<2> .number
自动将用户的输入值转化为数值类型

<input v-model.number="msg">

<3> .trim
自动过滤用户输入的首尾空格

<input v-model.trim="msg">

13. v-on
v-on 主要用来监听 dom 事件,以便执行一些代码块。表达式可以是一个方法名。
简写为:【 @ 】

<div id="app">
    <button @click="consoleLog"></button>
</div>
<script>
    var app = new Vue({
        el: '#app',
        methods:{
            consoleLog:function (event) {
                console.log(1)
            }
        }
    })
</script>

事件修饰符

  • .stop​ 阻止事件继续传播
  • .prevent​ 事件不再重载页面
  • .capture​ 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
  • .self ​只当在 ​event.target​ 是当前元素自身时触发处理函数
  • .once​ 事件将只会触发一次
  • .passive​ 告诉浏览器你不想阻止事件的默认行为
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
 
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
 
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
 
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
 
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
 
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
 
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
 
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用v-on:click.prevent.self阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

3、v-if 和 v-show 有什么区别?

共同点:​v-if​ 和 ​v-show​ 都能实现元素的显示隐藏

区别:

    1. v-show 只是简单的控制元素的 display 属性,而 v-if 才是条件渲染(条件为真,元素将会被渲染,条件为假,元素会被销毁);

    2.  v-show 有更高的首次渲染开销,而 v-if 的首次渲染开销要小的多;

    3. v-if 有更高的切换开销,v-show 切换开销小;

    4. v-if 有配套的 v-else-if 和 v-else,而 v-show 没有

    5. v-if 可以搭配 template 使用,而 v-show 不行

四、Vue核心思想:数据驱动、组件化

1、数据驱动

传统的前端数据交互是用 Ajax 从服务端获取数据,然后操作 DOM 来改变视图;或者前端交互要改变数据时,又要再来一次上述步骤,而手动操作 DOM 是一个繁琐的过程且易出错。Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 Javascript 库,专注于 View 层。它让开发者省去了操作 DOM 的过程,只需要改变数据。Vue 会通过 Dircetives 指令,对 DOM 做一层封装,当数据发生改变会通知指令去修改对应的 DOM,数据驱动 DOM 变化,DOM 是数据的一种自然映射。Vue 还会对操作进行监听,当视图发生改变时,vue 监听到这些变化,从而改变数据,这样就形成了数据的双向绑定。Vue 是一种 MVVM 框架。而 DOM 是数据的一个种自然映射。传统的模式是通过 Ajax 请求从 model 请求数据,然后手动的触发 DOM 传入数据修改页面。Vue 中,Directives 对 view 进行了封装,当 model 里的数据发生变化是,Vue 就会通过 Directives 指令去修改 DOM。同时也通过 DOM Listener实现对视图 view 的监听,当DOM 改变时,就会被监听到,实现 model 的改变,实现数据的双向绑定。

2、组件响应原理数据(model)改变驱动视图(view)自动更新

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

3、组件化

扩展 HTML 元素,封装可重用的代码。每一个组件都对应一个 ViewModel。页面上每个独立的可视/可交互区域都可以视为一个组件。每个组件对应一个工程目录,组件所需要的各种资源在这个目录下就进维护。页面是组件的容器,组件可以嵌套自由组合形成完整的页面。

组件化实现了扩展 HTML 元素,封装可用的代码。页面上每个独立的可视/可交互区域视为一个组件;每个组件对应一个工程目录,组件所需要的各种资源在这个目录下就近维护;页面不过是组件的容器,组件可以嵌套自由组合形成完整的页面。

五、Vue 生命周期

六、组件中 data 为什么是一个函数?

为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?

// data
data() {
  return {
	message: "子组件",
	childName:this.name
  }
}
 
// new Vue
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: {App}
})

因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。

 

七、Vue 组件间通信有哪几种方式?

Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。

(1)props / $emit 适用 父子组件通信

这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。

(2)ref 与 $parent / $children 适用 父子组件通信

  • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
  • $parent / $children:访问父 / 子实例

(3)EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通信

这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。

(4)$attrs/$listeners 适用于 隔代组件通信

  • $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind=”$attrs” 传入内部组件。通常配合 inheritAttrs 选项一起使用。
  • $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件

(5)provide / inject 适用于 隔代组件通信

祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

(6)Vuex 适用于 父子、隔代、兄弟组件通信

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  • 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

八、computed 和 watch 的区别和运用的场景?

computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;运用场景:

  • 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
  • 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

九、虚拟 DOM 

优点:

  • 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
  • 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
  • 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。

缺点:

  • 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。

虚拟 DOM 实现原理:

虚拟 DOM 的实现原理主要包括以下 3 部分:

  • 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
  • diff 算法 — 比较两棵虚拟 DOM 树的差异;
  • pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。

十、vue-router 路由模式有几种?

  1.  ​Hash​:      使用 URL 的 hash 值来作为路由。支持所有浏览器。
  2.  ​History​:   以来 HTML5 History API 和服务器配置。参考官网中 HTML5 History 模式
  3.  ​Abstract​: 支持所有 javascript 运行模式。如果发现没有浏览器的 API,路由会自动强制进入这个模式。

十一、delete和Vue.delete删除数组的区别

delete 只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。Vue.delete 直接删除了数组 改变了数组的键值。

十二、SPA 单页面的理解,它的优缺点分别是什么?

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。优点:

  • 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
  • 基于上面一点,SPA 相对对服务器压力小;
  • 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;

缺点:

  • 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
  • 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
  • SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。

 

十三、简述Vue的响应式原理

当一个 Vue 实例创建时,vue 会遍历 data 选项的属性,用 Object.defineProperty 将它们转为 getter/setter 并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

十四、Vue中如何在组件内部实现一个双向数据绑定?

假设有一个输入框组件,用户输入时,同步父组件页面中的数据具体思路:父组件通过 props 传值给子组件,子组件通过 $emit 来通知父组件修改相应的 props 值,具体实现如下:

import Vue from 'vue'
 
const component = {
  props: ['value'],
  template: `
    <div>
      <input type="text" @input="handleInput" :value="value">
    </div>
  `,
  data () {
    return {
    }
  },
  methods: {
    handleInput (e) {
      this.$emit('input', e.target.value)
    }
  }
}
 
new Vue({
  components: {
    CompOne: component
  },
  el: '#root',
  template: `
    <div>
      <comp-one :value1="value" @input="value = arguments[0]"></comp-one>
    </div>
  `,
  data () {
    return {
      value: '123'
    }
  }
})

可以看到,当输入数据时,父子组件中的数据是同步改变的:

我们在父组件中做了两件事,一是给子组件传入 props,二是监听 input 事件并同步自己的 value 属性。那么这两步操作能否再精简一下呢?答案是可以的,你只需要修改父组件:

template: `
    <div>
      <!--<comp-one :value1="value" @input="value = arguments[0]"></comp-one>-->
      <comp-one v-model="value"></comp-one>
    </div>
  `

v-model 实际上会帮我们完成上面的两步操作。

十五、 Vue中如何监控某个属性值的变化?

比如现在需要监控 data 中,obj.a 的变化。Vue 中监控对象属性的变化你可以这样:

watch: {
      obj: {
      handler (newValue, oldValue) {
        console.log('obj changed')
      },
      deep: true
    }
  }

deep 属性表示深层遍历,但是这么写会监控 obj 的所有属性变化,并不是我们想要的效果,所以做点修改:

watch: {
   'obj.a': {
      handler (newName, oldName) {
        console.log('obj.a changed')
      }
   }
  }

还有一种方法,可以通过 computed 来实现,只需要:

computed: {
    a1 () {
      return this.obj.a
    }
}

利用计算属性的特性来实现,当依赖改变时,便会重新计算一个新值。

推荐好课:vue2.x微课Vue项目实战精讲Vue.js三天学习实战教程

Java实现pdf和Excel的生成及数据动态插入与导出

thbcm阅读(162)

一、序言

Excel、PDF的导出、导入是我们工作中经常遇到的一个问题,刚好今天公司业务遇到了这个问题,顺便记个笔记以防下次遇到相同的问题而束手无策。

公司有这么两个需求:

需求一、给了一个表单,让把查出来的数据组装到表单中并且提供以PDF格式的下载功能。

需求二、将数据查出来以Excel表格的形式下载下来。

二、Java实现PDF的生成和数据动态插入、导出功能

1、第一步:PDF制作模板

因为 PDF 常用的软件不让支持编辑,我们就先使用 WPS 以 Word 的形式进行编辑制作出与客户需求一样的样式,然后直接另存为 .pdf 的形式如下图所示:

a.Word 里面制作模板

b.更改名字为 .pdf形式


c.这时需要用到一个叫:Adobe Acrobat DC 的软件,具体操作如下:

用 Adobe Acrobat DC 打开我们刚才改过名字的 PDF 文件,点击右下角的“更多工具”按钮

到下面这个页面再点击“准备表单”按钮

d.接下来就需要详细的配置你的数据源了

数据源即:你代码中实体类中对应的数据(注意字段一定要一一对应),配置完毕就可以保存进行下面的代码编写工作了。

2、代码的编写

假定我们实体类什么的都已经编写完成、数据通过前端传入获取、模板位置在E盘根目录下名字为:车辆维修审批单.pdf

导入 jar 包:

<!-- PDF导出-->
<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13</version>
</dependency>

实现生成 PDF、数据插入、导出

@RegisterToSMP(serviceDisplay = "预览页面PDF下载")      
@RequestMapping(value = "/DM/gwclwxsq/qygl/exportPDF$m=query.service",method =RequestMethod.POST) 
public String exportPdf(@RequestBody GwclwxsqBean gwclwxsqBean , HttpServletResponse response) throws UnsupportedEncodingException {            
    // 1.指定解析器
    System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
            "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
    String filename="车辆维修审批单.pdf";
    String path="e:/";
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment;fileName="
            + URLEncoder.encode(filename, "UTF-8"));
    OutputStream os = null;
    PdfStamper ps = null;
    PdfReader reader = null;
    try {
        os = response.getOutputStream();
        // 2 读入pdf表单
        reader = new PdfReader(path+ "/"+filename);
        // 3 根据表单生成一个新的pdf
        ps = new PdfStamper(reader, os);
        // 4 获取pdf表单
        AcroFields form = ps.getAcroFields();
        // 5给表单添加中文字体 这里采用系统字体。不设置的话,中文可能无法显示
        BaseFont bf = BaseFont.createFont("C:/WINDOWS/Fonts/SIMSUN.TTC,1",
                      BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        form.addSubstitutionFont(bf);
        // 6查询数据================================================
        Map<String, String> data = new HashMap<String, String>();
              data.put("commitTime", gwclwxsqBean.getCommitTime());
              data.put("driver", gwclwxsqBean.getDriver());
              data.put("carId", gwclwxsqBean.getCarId());
              data.put("carType", gwclwxsqBean.getCarType());
              data.put("repairAddress", gwclwxsqBean.getRepairAddress());
              data.put("repairCost",gwclwxsqBean.getRepairCost());
              data.put("project", gwclwxsqBean.getProject());
              data.put("fwbzzxfzrYj", gwclwxsqBean.getFwbzzxfzrYj());
              data.put("fgldspYj", gwclwxsqBean.getFgldspYj());
              data.put("remarks", gwclwxsqBean.getRemarks());           
         // 7遍历data 给pdf表单表格赋值
        for (String key : data.keySet()) {
            form.setField(key,data.get(key).toString());
        }
        ps.setFormFlattening(true);       
        log.info("*******************PDF导出成功***********************");
    } catch (Exception e) {          log.error("*******************PDF导出失败***********************");
        e.printStackTrace();
    } finally {
        try {
            ps.close();
            reader.close();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return null;
}

3.测试

二、Java实现Excel生成和数据插入、导出

这个比较简单,直接上代码(假定你的实体类、查询什么的都已经写好)注意:实体类一个是你自己的数据实体类还有一个是你导出时表格中对应的实体类。100道 Java中高级面试题汇总

我们以一个真实的公司业务来举个例子(一个统计疫情登记人员信息的Excel导出功能)

a.表头对应实体类 ExportYqfkdj.java

import lombok.Data;

/**
 * description: 
 * @author: zhouhong
 * @version: V1.0.0
 * @date: 2021年1月14日 下午3:05:54
 */
@Data
public class ExportYqfkdj {
    /**
     * 序号
     */
    private Integer xuhao;
    /**
     * 姓名
     */
    private String xingming;  
    /**
     * 证件号码
     */
    private String zjhm;
    /**
     * 联系电话
     */
    private String lxdh;    
    /**
     * 申请人工作单位
     */
    private String sqrGzdw;    
    /**
     * 是否接触过疑似病例
     */
    private String sfjcgysbl;
    /**
     * 当前是否与居家隔离人员同住
     */
    private String sfyjjglrytz;    
    /**
     * 当前状态
     */
    private String dqzt;
    /**
     * 当前健康状态
     */
    private String dqjkzt;

    /**
     * 当前体温
     */
    private String dqtw;
    /**
     * 当前所在地址
     */
    private String dqszdz;
    /**
     * 当前居住地址
     */
    private String dqjzdz;
    /**
     * 提交时间
     * */
    private String tjsj;
}

b.Service 层

/**
 * 导出
 * @param yqfkdjBean
 * @author zhouhong
 * @return 
 * @throws Exception
 */
@Transactional(rollbackFor = { Exception.class })
public DataResult exporYqfkdj(YqfkdjBean yqfkdjBean) throws Exception {
    DataResult result = new DataResult();
    List<ExportYqfkdj> list = new ArrayList<ExportYqfkdj>();
    try {
        /* 查询导出信息 */
        result = getYqfkMhCXQuery(yqfkdjBean);
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddhhmmssSSS");
        for (int i = 0; i < result.getTotalcount(); i++) {
            ExportYqfkdj dmKhfwdcDtjlZxDto = new ExportYqfkdj();
            dmKhfwdcDtjlZxDto = ObjectUtil.parsePojo(result.getResults().get(i), ExportYqfkdj.class);
            dmKhfwdcDtjlZxDto.setXuhao(i + 1);
            list.add(dmKhfwdcDtjlZxDto);
        }
        String filepath = "D:/疫情防控信息" + df.format(new Date()) + ".xlsx";
        if (System.getProperty(YqfkdjUtils.Wjdz.NAME).toLowerCase().startsWith(YqfkdjUtils.Wjdz.LI)
                || System.getProperty(YqfkdjUtils.Wjdz.NAME).toLowerCase().startsWith(YqfkdjUtils.Wjdz.LIN)) {
            filepath = "/home/Tomcat/temp/" + df.format(new Date()) + ".xlsx";
        }
        EasyExcel.write(filepath, ExportYqfkdj.class).head(head()).sheet().doWrite(list);
        result.setResults(list);
        result.setSuccess(true);
        result.setMsg(filepath);
    } catch (Exception e) {
        result.setSuccess(false);
        result.setMsg(YqfkdjUtils.Cytx.DCSB);
        e.printStackTrace();
        throw e;
    }
    return result;
}
/**
 * 疫情防控信息导出表头
 * @author zhouhong
 * @return List<List<String>>
 */
private List<List<String>> head() {
    List<List<String>> list = new ArrayList<List<String>>();
    List<String> head0 = new ArrayList<String>();
    head0.add("序号");
    List<String> head1 = new ArrayList<String>();
    head1.add("姓名");
    List<String> head2 = new ArrayList<String>();
    head2.add("证件号码");
    List<String> head3 = new ArrayList<String>();
    head3.add("联系电话");
    List<String> head4 = new ArrayList<String>();
    head4.add("工作所在单位");
    List<String> head5 = new ArrayList<String>();
    head5.add("是否接触疑似病例");
    List<String> head6 = new ArrayList<String>();
    head6.add("是否与隔离人员同住");
    List<String> head7 = new ArrayList<String>();
    head7.add("当前状态");
    List<String> head8 = new ArrayList<String>();
    head8.add("当前健康状态");
    List<String> head9 = new ArrayList<String>();
    head9.add("体温(°C)");
    List<String> head10 = new ArrayList<String>();
    head10.add("当前所在地址");
    List<String> head11 = new ArrayList<String>();
    head11.add("当前居住地址");
    List<String> head12 = new ArrayList<String>();
    head12.add("提交时间");
    list.add(head0);
    list.add(head1);
    list.add(head2);
    list.add(head3);
    list.add(head4);
    list.add(head5);
    list.add(head6);
    list.add(head7);
    list.add(head8);
    list.add(head9);
    list.add(head10);
    list.add(head11);
    list.add(head12);
    return list;
}

c.Controller 层

@RegisterToSMP(serviceDisplay = "疫情防控查询导出")
@RequestMapping(value = "/DM/yqfkdj/gr/yqfkdjdc$m=export.service", method = RequestMethod.POST)
public void exportKhfxxx(@RequestBody YqfkdjBean yqfkdjBean, HttpServletResponse resp) throws Exception {
    DataResult result = new DataResult();
    try {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddhhmmssSSS");
        result = yqfkdjService.exporYqfkdj(yqfkdjBean);
        String filepath = result.getMsg().replace("\"", "");
        File file = new File(filepath);
        String filename = "疫情防控信息" + df.format(new Date()) + ".xlsx";
        InputStream fis = new BufferedInputStream(new FileInputStream(filepath));
        byte[] buffer = new byte[fis.available()];
        fis.read(buffer);
        fis.close();
        resp.reset();
        resp.setHeader("Content-Disposition",
                "attachment;filename=" + new String(filename.replaceAll(" ", "").getBytes("gbk")));
        resp.setHeader("Content-Length", "" + file.length());
        OutputStream os = new BufferedOutputStream(resp.getOutputStream());
        resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        // 输出文件
        os.write(buffer);
        os.flush();
        os.close();
    } catch (Exception e) {
        e.printStackTrace();
        log.info(YqfkdjUtils.Cytx.DCSB);
        throw e;
    }
}

d.测试

已经全部完成 PDF 和 Excel 的生成、插入、导出功能。

批评代码而不是人!15年程序员的职场箴言

thbcm阅读(163)

获得成功必然是一个不断学习成长的过程。在这个过程中,来自别人的指导意见以及经验之谈会给予我们一些启发和提示,从而避免许多不必要的弯路。择其善者而从之,其不善者而改之。

在从事软件开发工作的15年中,我经历了许多不同的阶段。在某些时候,我恨不得把所有的软件开发工作都丢进地狱,宁愿跑到深山老林里去喂牛。而在其他时候,软件开发工作的一切对我来说是那么的完美。今天,我终于让我的职业生涯和个人生活达到了平衡,我可以告诉自己我非常喜欢软件开发,但是我不能忘记它并非一直这样完美,而且也并非对每个人都是如此完美。

本文将分享在这15年中我学到的一些经验,正是这些经验让我的职业生涯与个人生活达到了和谐共存。

如何达到和谐共存呢?以下是我的经验分享:

一、经常换工作

当开始从事软件开发这一职业时,通常的入门都是从初级开发人员开始,但是初级开发人员的薪酬通常都很低。无论你多么努力,想在一家公司取得快速的进步并不是一件容易的事,尤其是如果你所在的是一家小公司的话。如果你想快速提高你的技能和你的薪水,那么,就需要经常换工作,寻找一个更好的职位。

如果你的生活中没有太多的负担,那就不要犹豫换工作。

二、为你自己而不是为公司做好工作

努力学习,努力提高技能,努力做正确的事情,但记得要为自己而不是为公司而做。因为当你的工作出了问题或者当公司不再需要你的时候,你就是一个可以牺牲的数字。

你的时间属于你自己,它是你最宝贵的财富。

三、如果你被解雇,就把它当作一个做你不敢做的事情的好机会

在某个时间段你在一份平庸的职位中毫无激情地工作着,而在另一个时间段你在另一家公司找到了一份不错的工作。有时候你会害怕失去工作,但有时候被解雇可能是发生在你身上最好的事情。

几年前,我曾经有一份好工作,薪水很高,也很轻松。然而经济危机来了,公司解雇了一半的无项目可做的员工。我就是其中之一。当时我认为这可能是发生在我身上最糟糕的事情,因为这份工作让我的生活改变了很多,这些改变可能是影响我生命的许多方面的最好的事情。被解雇这件事让我非常失望,因为我为公司做了一切,为了公司我牺牲了很多私人时间。

现在我到了一个充满活力的公司,我可以做我想做的工作,做我喜欢的项目。这份新的工作让我觉得非常吸引人,星期一也不再那么烦人了。

四、你可以在工作中交到好朋友,

但当你换工作时,这些朋友中的大多数都会失去

这些年来,我在工作中认识了很多人。在我曾经工作过的公司里,我们每年都会一起参加公司的晚会,公司的午餐会,以及每个人都会去的公司“活动”,但所有这些美好的和谐并不像我们想象的那样真实。

许多人都只是想取悦老板,并且对那些不在现场的同事和上司吹毛求疵。我意识到我在这些活动上浪费了多少时间。现在我只去参加那些我想去参加的活动,不再顾忌别人怎么想。

与以前的同事保持联系很困难,随着时间的推移,我和他们中的大多数都失去了联系。虽然我也交了一些好朋友,但在大多数情况下,在换工作的时候,这些人际关系就消失了,我的好朋友大多数都不是对我的职业生涯很重要的人。

五、如果你安于现状,你就会失去机会

在你的职业生涯中,你所做的最糟糕的事情之一就是在工作中变得过于安逸而不思进取。如果你变得安于现状,你将害怕你的职业上的任何变化,也会害怕你的公司可能发生的任何变化。

我的一个朋友就是这样,他在一份舒服的工作上已经做了十多年了;他很安逸,不再想学什么新鲜东西,这导致他在职业上或生活中可以得到提升的机会越来越少,随着时间的推移,我看到他越来越害怕——当然,他讨厌星期一。

我也在一份很舒服的工作上干了将近两年,这份工作让我学不到什么新东西。而且这份工作对我来说非常容易,每天都有很多空闲时间。这样的工作让我觉得是在抵押未来,为此我决定换份工作。

六、批评代码,而不要批评人

如果你的同事做错了什么事,你可以有很多方式指出来。但是永远不要指责人,因为这样做只会让你制造敌人,不仅是你个人的敌人,而且是你职业的敌人。你要尽量尝试帮助每个人,如果你认为你能够做到的话,就帮他改进代码,但是你需要向他解释原因,不要强行修改。记住,生活都是在兜圈子,发生在你同事身上的事同样会发生在你身上。

一些开发人员倾向于对他们所做的事情产生一种巨大的依恋感,好像他们的作品代表了他们的“宝贝”,对它的任何批评都是针对个人的,或者被解释为对他们自己的批评。

记住,对代码的批评不是对人的批评。仅仅因为有人在过去写了糟糕的代码,并不意味着他现在是一个差劲的开发人员。代码是坏的,但人不见得是。任何事情都是可以改进的。

七、改善工作工具和工作空间

这一点非常重要。如果你的工作电脑速度很慢,你完成事情的时间会更长,时间就是金钱。如果你的显示器很小,你将无法将屏幕划分为不同的屏幕,这也会浪费你的时间。

现在我每天在家里工作,我决定买一把好椅子,一个大显示器(以便我在编程时把它分成几个屏幕),我还要买一台功能强大的电脑。这可能看起来很贵,但随着时间的推移,它会为你节省很多金钱。

现在我自己一个人可以做更多的项目,我可以更快地完成我必须完成的任务,我的背也不会受伤。

而且,我有更多的时间做运动,所以我不需要做背部按摩,晚上我的眼睛可以得到更多的休息,我可以享受在床上看书的乐趣。

八、如果你有带薪或者不带薪的假期,可以的话就休假吧

时间是你拥有的最宝贵的东西。工作和金钱不是你的生活的全部。而且,每年休个假对你有好处,你可以借此机会思考一下你对自己正在做的工作是否满意,或者放下日常的工作,试试做做不同的事情。

此外,休息可以帮助你了解自己正在做什么,以及对自己做的事情满意还是不满意。

我总是在空闲时间有最好的想法,我一生中最好的决定都是在空闲时间做出的。

九、避免技术债务

“技术债务”是软件开发中的一个概念,它指的是由于采用简单(有限)的解决方案而不是使用需要更长时间的更好方法而导致的额外工作的隐含成本。尽管如此,我还是更喜欢沃德·坎宁安(Ward Cunningham)提出的这个比喻,即当你在处理技术债务时,把它视为金融债务:而增加新特性的额外努力相则当于金融债务的利息。

如果你快速解决了一个问题,没有经过足够的思考,没有运用更好的做法,将来你就要为此付出代价,你将不得不花费几个小时的空闲时间来解决它。

十、学会即兴发挥,不要把变化看得太悲观

我记得我在进入这个行业工作的前几个月,我相信项目一定会定义明确,客户一定知道他们想要什么。然而,我很快意识到事实并非如此。

这让我感到焦虑,因为我想让工作按时完成,而很多时候这是不可能的。你要学会接受情况一直会变化,很多时候这些变化会导致你不能按时完成一个项目,但这不是你的错。放轻松,拥抱这些变化。

十一、如果你不在工作中做决定,别人会替你做决定

如果你想成为那个决定在一个项目中使用一项新技术,或者使用一个新方法的人,在将你的决定告诉同事之前,你必须知道你想使用的新技术或新方法。

如果你不这样做,他们可能会把他们的技术强加于你,或者否决你的想法。

十二、保持你自己的节奏

软件开发的世界日新月异,你要以平常心待之,放松心情,不断学习和成长,始终以自己的速度前进。如果你不能学到所有的东西,不要沮丧。关键是要完美地学习重要和基本的东西,并跟上其他技术的发展。

十三、学习如何阅读文档

匆匆忙忙地使用某些东西会使你无法很好地阅读文档或正确理解 API。

如果你想保证你做的事情正确而不浪费时间,不要试图走捷径。好好阅读文档。否则,你可能不会以最好的方式工作。

对 Stack Overflow、Google 和其他来源的源代码保持批评态度。不要照搬解决方案,要理解它。

十四、学会放下工作

不要把工作上的问题带回家。如果有一 bug 或者一个你不知道如何解决的技术问题,把它留到第二天。你会为自己节省很多时间,而且你不会因为整天都在解决问题而讨厌你的工作。休息的头脑更有效率。

如果你不这样做,焦虑会让你无法入睡,第二天你会面临同样的问题,同时昏昏欲睡。

这一点不太付诸实践,但它却是我学到的最有价值的经验之一。

结论:

我在这篇文章中所写的是个人经验,可能看起来显而易见。但是在很多情况下,即使我们知道这些方法如何改进我们的工作和生活,我们也不会去做,而生活最终会迫使我们去做。不要把它留到最后一刻;如果我在一开始工作时就运用了这些相同的技巧,我相信在随后的工作中我会节省很多时间和少很多弯路。

深入了解 java 泛型的含义

thbcm阅读(170)

泛型

实例解释为什么引入泛型

// 不使用泛型
List list = new ArrayList();
list.add("coding");          // 集合中可以添加不同类型的元素(集合就是这么设计的,主要是为了实现通用性,但也带来了弊端,泛型就是为了解决这个产生的)
list.add(1024);                         // 元素丢进集合中全部变成了Object
String result1 = list.get(0);           // 此行代码编译器不过
String result2 = (String)list.get(0);    // 如果想要还原集合中元素需要使用强制类型转换,强制类型转换可能引发异常,因为集合中元素类型有多种。
String result3 = (String)list.get(1);    // 此行代码运行时会报类型转换异常ClassCastException,所以说集合不使用泛型来限制数据类型的话很容易产生bug
System.out.println(result2);

泛型的定义

1、通过上面例子的引入,我们可以总结出:泛型本质上是参数化类型,我们可以为类,接口,方法指定一个类型参数,通过这个参数来限制操作的数据类型,从而保证类型转换的绝对安全。

2、基本用法:泛型集合

// 使用泛型
List<String> str1 = new ArrayList<String>();     // 在<>中指定集合中元素类型
str1.add("java");
str1.add(1024);            // 此行代码编译器报错,也就是限制了集合中类型只能为String类型,避免了强制类型转化时出现异常

泛型的其它用处

  • 上面的实例告诉我们泛型可以解决集合中存在的不足之处,但泛型的作用不止于此。

泛型方法

  • 泛型方法在定义的时候需要在方法的返回类型之前加上 <T>,这个T可以换成其他字母,T代表方法的参数是什么类型,T可以表示任何包装类型,不支持基本类型。
  • 泛型方法并不显式指定其参数的数据类型,而是在使用该方法时才确定数据类型。这样带来的好处就是一个泛型方法可以接受不同类型的输入参数,减少了重复代码。下面的例子仅供说明。
public class Generics_Test {
	// 泛型方法
	public static <T> void print(T[] arr) {
		System.out.println(arr[0]);
	}
	
	public static void main(String[] args) {
		String[] str2 = {"test"};
		print(str2);
		Integer[] num = {1024};
		print(num);
	}
}

泛型类

  • 引入背景:当一个类中有多个泛型方法,为了避免每个泛型方法在声明的时候都需要加上 <T> 来说明其是泛型方法,就引入了泛型类。
// 泛型类
public class Generics_Test<T> {

	public static <T> void print(T[] arr) {      // static 仍然需要显示声明<T>,否则会报错,因为静态方法不要类实例化就能调用。
		System.out.println(arr[0]);
	}
	
	public void printf(T[] arr) {               // 普通方法不需要再声明<T>
		System.out.println(arr[0]);
	}
	
	public static void main(String[] args) {
	
		Generics_Test<String> gt = new Generics_Test<String>();
		String[] str2 = {"test"};
		gt.printf(str2);
		
		Generics_Test<Integer> gt1 = new Generics_Test<Integer>();
		Integer[] num = {1024};
		gt1.printf(num);
	}
}

泛型的高级用法

通配符:<?>

  • 使用 <?> 可以不用指定参数类型,即不用在方法的返回值前声明 <T>
// 通配符<?>
public class Generics_Test {

	public static void print(List<?> arr) {       // 使用 <?> 可以不用指定参数类型,即不用在方法的返回值前声明<T>
		Object result = arr.get(0);
		System.out.println(result);
	}
	
	public static void main(String[] args) {
		List<String> str1 = new ArrayList<String>();
		str1.add("coding");
		print(str1);
	}
}

通配符: <? extends anyClass>

  • java的泛型默认是可以使用任何包装类型来实例化一个泛型类对象
public class Generics_Test<T extends Object> {  
	public static void main(String[] args) {
		// 因为object是所有类型的父类,所以可以使用任何包装类型来实例化一个泛型类对象
		Generics_Test<ArrayList> arr1 = new Generics_Test<ArrayList>();
		Generics_Test<LinkedList> link1 = new Generics_Test<LinkedList>();
		Generics_Test<HashMap> str1 = new Generics_Test<HashMap>();
	}
}
  • 限制泛型类的可用类型。​T extends anyClass​:该泛型类接受的类型必须继承或实现 anyClass(其中 anyClass 表示类或接口)
public class Generics_Test<T extends List> {   
	public static void main(String[] args) {
		Generics_Test<ArrayList> arr1 = new Generics_Test<ArrayList>();
		Generics_Test<LinkedList> link1 = new Generics_Test<LinkedList>();
		Generics_Test<List> list1 = new Generics_Test<List>();      
		Generics_Test<HashMap> hashmap1 = new Generics_Test<HashMap>(); // 此行代码会报错,因为HashMap没有实现List接口
	}
}

泛型类的继承

public class Generics_Test<T1>{
}
class SonGenericsClass<T1,T2,T3> extends Generics_Test<T1>{
}

推荐好课:Java微课java面试基础题应知应会

微软Edge浏览器将在4月份替换为Chromium内核

thbcm阅读(184)

微软公司(Microsoft)4月的 Windows 10 累积更新将删除旧版、过时的 Edge 浏览器,并将其替换为 2020 年首次亮相的基于 Chromium 的新版本。

作者:Gregg Keizer(《计算机世界》高级记者)

来源:《计算机世界》| PST 2021年2月8日下午1:46

翻译:W3Cschool

微软已经告知用户,Windows 10 的四月累积更新(计划于当月的“周二补丁”于 4 月 13 日到达)将删除原始的,现在已经过时的 Edge 浏览器,并将其替换为较新的一年前首次亮相的基于 Chromium 的 Edge 版本。

接受 4 月更新的用户(包括每个周二补丁的更新)将包括本月的安全漏洞修复程序,他们还将收到 Chromium Edge 并丢失旧版 Edge 。较旧的浏览器于 2015 年中启动,成为 Windows 10 的默认浏览器。

跳过 4 月的更新将无济于事,因为 4 月 13 日之后发布的每个累积更新还将包括旧的 Edge-gone 新的 Edge-here 进程。

4 月的更新将结束从旧版 Edge 到基于 Chromium 的浏览器的过渡,该浏览器于 2020 年 1 月 15 日在其稳定版中首次发布。

2020 年 8 月,Microsoft 阐明了如何终止旧版 Edge 的生命,包括 2021 年 3 月 9 日发布最终安全更新的日期。但是,华盛顿州雷德蒙德市的开发人员并未为较新的本地版本 Edge 设置删除日期。

由 Windows 10 20H2 驱动的设备(2020 年 10 月发布的升级)已经运行 Chromium Edge,因此不会重新安装浏览器。取而代之的是,4 月累积更新将仅清除旧版 Edge 的系统。

使用其他版本 Windows 10 且用户手动安装了 Chromium Edge 的 PC 将被视为相同。

微软还指出,应用 Windows 10 March Preview 更新(定于 3 月 16 日前后显示)的客户将发现 Chromium Edge 已安装,而旧版本已删除,因为该可选版本还将运行浏览器交换。

以上就是编程狮(w3cschool.cn)为你整理的关于 Windows 10 系统自带浏览器 Edge 的最新消息,英文原文来自 computerworld 。

2021 第一期 日常开发 26 个常见的 JavaScript 代码优化方案

thbcm阅读(188)

2021 第一期。

本篇文章整理了在日常开发中 30 个常见的 JavaScript 代码优化方案。

1、NUll、Undefined、”检查

我们在创建新变量赋予一个存在的变量值的时候,并不希望赋予​ null ​或 ​undefined​,我们可以采用一下简洁的赋值方式。

if(test !== null || test !== undefined || test !== ''){
  let a1 = test;
}

// 优化后
let a1 = test || ''

2、null 值检查并赋予默认值

let test = null;
let a1 = test || '';

3、undefined 值检查并赋予默认值

let test = undefined;
let a1 = test || '';

4、空值合并运算符(??)

空值合并操作符(??)是一个逻辑操作符,当左侧的操作数为 ​null​ 或者 ​undefined​ 时,返回其右侧操作数,否则返回左侧操作数。

const test= null ?? 'default string';
console.log(test);

console.log(test); // expected output: "default string"

const test = 0 ?? 42;
console.log(test); // expected output: 0

5、声明变量

当我们想要声明多个共同类型或者相同值的变量时,我们可以采用一下简写的方式。

let test1;
let test2 = 0;

//  优化后
let test1, test2 = 0;

6、if 多条件判断

当我们进行多个条件判断时,我们可以采用数组 ​includes​ 的方式来实现简写。

if(test === '1' || test === '2' || test === '3' || test === '4'){
  // 逻辑
}

// 优化后
if(['1','2','3','4'].includes(test)){
  // 逻辑处理
}

7、if…else 的简写

当存在一层或两层 ​if...else​嵌套时,我们可以使用三元运算符来简写。

let test = null;
if(a > 10) {
  test = true;
} else {
  test = false;
}

// 优化后
let test = a > 10 ? true : false;
// 或者
let test = a > 10;

8、多变量赋值

当我们想给多个变量赋不同的值的时候,我们可以采用一下简洁的速记方案。

let a = 1;
let b = 2;
let c = 3;

// 优化
let [a, b, c] = [1, 2, 3];

9、算术运算简写优化

当我们在开发中经常用到算数运算符时,我们可以使用一下方式进行优化和简写。

let a = 1;
a = a + 1;
a = a - 1;
a = a * 2;

// 优化
a++;
a--;
a *= 2;

10、有效值判断

我们经常会在开发中用到的,在这也简单整理一下。

if (test1 === true)
if (test1 !== "")  
if (test1 !== null)

// 优化
if (test1)

11、多条件(&&)判断

我们通常在项目中遇到条件判断后跟函数执行,我们可以使用一下简写方式。

if (test) {
 foo(); 
} 

//优化
test && foo();

12、多个比较 return

在 ​return​ 的语句中使用比较,可以将其进行缩写的形式如下。

let test;
function checkReturn() {
    if (!(test === undefined)) {
        return test;
    } else {
        return foo('test');
    }
}

// 优化
function checkReturn() {
    return test || foo('test');
}

13、Switch 的缩写

遇到如下形式的 ​switch​ 语句,我们可以将其条件和表达式以键值对的形式存储。

switch (type) {
  case 1:
    test1();
  break;
  case 2:
    test2();
  break;
  case 3:
    test();
  break;
  // ......
}

// 优化
var obj = {
  1: test1,
  2: test2,
  3: test
};

obj[type] && obj[type]();

14、for 循环缩写

for (let i = 0; i < arr.length; i++)

// 优化
for (let i in arr) or  for (let i of arr)

15、箭头函数

function add() {
  return a + b;
}

// 优化
const add = (a, b) => a + b;

16、短函数调用

const data1 = [1, 2, 3];
const data2 = [4 ,5 , 6].concat(data1);

// 优化
const data2 = [4 ,5 , 6, ...data1];

17、数组合并与克隆

const data1 = [1, 2, 3];
const data2 = [4 ,5 , 6].concat(data1);

// 优化
const data2 = [4 ,5 , 6, ...data1];

数组克隆:

const data1 = [1, 2, 3];
const data2 = test1.slice()

// 优化
const data1 = [1, 2, 3];
const data2 = [...data1];

18、字符串模版

const test = 'hello ' + text1 + '.'

// 优化
const test = `hello ${text}.` 

19、数据解构

const a1 = this.data.a1;
const a2 = this.data.a2;
const a3 = this.data.a3;

// 优化
const { a1, a2, a3 } = this.data;

20、数组查找特定值

数组按照索引来查找特定值,我们可以通过逻辑位运算符 ​​ 来代替判断。


“~”​运算符(位非)用于对一个二进制操作数逐位进行取反操作

if(arr.indexOf(item) > -1) 

// 优化
if(~arr.indexOf(item))

// 或
if(arr.includes(item))

21、Object.entries()

const data = { a1: 'abc', a2: 'cde', a3: 'efg' };
Object.entries(data);

/** 输出:
[ [ 'a1', 'abc' ],
  [ 'a2', 'cde' ],
  [ 'a3', 'efg' ]
]
**/

22、Object.values()

我们可以通过 ​Object.values()​ 将对象的内容转化为数组。如下:

const data = { a1: 'abc', a2: 'cde' };
Object.values(data);

/** 输出:
[ 'abc', 'cde']
**/

23、求平方

Math.pow(2,3); 

// 优化
2**3;

24、指数简写

for (var i = 0; i < 10000; i++)

// 优化
for (var i = 0; i < 1e4; i++) {

25、对象属性简写

let key1 = '1'; 
let key2 = 'b';
let obj = {key1: key1, key2: key2}; 

// 简写
let obj = {
  key1, 
  key2
};

26、字符串转数字

let a1 = parseInt('100'); 
let a2 = parseFloat('10.1'); 

// 简写 
let a1 = +'100'; 
let a2 = +'10.1';

推荐微课:JavaScript微课,JavaScript基础实战,JavaScript面向对象编程

HTML+CSS+JS详解

thbcm阅读(157)

Web概述

Web三要素:浏览器,服务器,HTTP协议

HTML工作原理:HTML是部署在服务器上的文本文件,根据HTTP协议浏览器发出请求给服务器,服务器做出响应给浏览器返回一个HTML,浏览器解释执行HTML,从而显示内容

什么是HTML?

HTML是超文本标记语言(Hyper Text Markup Language),一种纯文本类型的语言,用来设计网页的标记语言,用该语言编写的文件以.html或者.htm为后缀,由浏览器解释执行,在HTML的页面上可以嵌套脚本语言编写程序段,如JavaScript

HTML标签

HTML标签通常也被称为HTML标记,HTML元素;HTML标签是由尖括号包围的关键字,比如<html>,HTML标签通常是成对出现的,比如<b></b>,标签对中的第一个标签为开始标签,第二个标签为结束标签,开始标签和结束标签也被称为开放标签和闭合标签

HTML注释:

<!–注释内容 –>

可以将注释插入 HTML 代码中,这样可以提高其可读性,使代码更易被人理解。浏览器会忽略注释,也不会显示它们

HTML文档类型:

<!DOCTYPE>声明:HTML由多个不同的版本,只有完全明白页面中的使用的确切HTML版本,浏览器才能完全正确的显示HTML页面,这就是<!DOCTYPE>的意义;

<!DOCTYPE>不是HTML的标签,它为浏览器提供一项信息,即HTML是用什么版本所写的

HTML<head>标签:

定义:<head>标签用于定义文档的头部,是所有头部元素的容器,<head>中的元素可以引用脚本,指示浏览器在哪里找到样表式,提供元信息等等

文档的头部描述了文档的各种信息和属性,包括文档的标题,在web中的位置以及和其他文档的关系等,绝大多数文档的头部包含的数据都不会真正的作为内容显示给读者

以下这些标签可用在head部分:<title>,<meta>,<link>,<style>,<script>,<base>

文本元素:

作用:文本时网页上的重要组成部分,直接书写的文本会用浏览器默认的样式显示

文本元素列表:是包含在文本元素中的文本,则会被显示为元素所拥有的样式

HTML标题:

标题是通过<h1>~<h6>标签进行定义的,目的是为了能够以醒目的方式显示,<h1>定义最大标题,<h6>定义最小标题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H0qWULYZ-1613216632726)(C:\Users\Melody\AppData\Roaming\Typora\typora-user-images\image-20210213192107880.png)]

HTML段落:

段落是通过<p>标签进行定义的,<p>元素提供了结构化文本的一种方式,<p>元素对中的文本会以单独的段落显示,与前后文本换行分开,添加一段额外的垂直空白距离,作为行间距

HTML列表:

列表是将具有相似特征或先后顺序的几行文字进行对齐排列,所有列表都是由列表类型和列表项组成

列表类型:

有序列表 ​<ol>​:用于列出表面上有特定次序的一些项目,其中只能包含列表项<li>

无序列表 ​<ul>​:用于列出页面上没有特定次序的一些项目,也只能包含具体列表项元素<li>

列表项:用来表示列表具体的内容 <li>

HTML列表嵌套:将列表元素嵌套使用,可以建多层列表

HTML分区元素:

用于元素的一些分组,常用于页面布局,块分区元素<div></div>,行内分区元素<span></span>

元素显示方式:

​ 块级元素:默认情况下,块级元素独占一行,即元素前后都会自动换行,比如<p>,<div>

​ 行内元素:不会换行,与其他行内元素位于同一行

​ <span>​元素是内联元素,可用作文本的容器,无特定的含义

​ ​<i>​元素定义斜体字

​ ​<em>​定义着重文字

​ ​<del>​用来定义带删除线的文字

​ ​<u>​用来定义带下划线的文字

​ 空格折叠:默认情况下,HTML中的多个空格,多个换行符会压缩成单个空格,即只显示一个空格

​ 实体引用:空格:&nbsp;(小于号)<:< (大于号 ) >:&gt;

HTML图像:

使用<img>元素可以将图片添加到页面

语法:<img src=“url” >

常用属性:width,height

src指的是”source”,原属性的值是图像的URL地址,但是地址值分为绝对路径和相对路径

绝对路径:文件从最高级目录下开始的完整路径,无论当前路径是什么,使用绝对路径后总能找到要连接的文件

相对路径:文件的位置是相对于当前的文件位置,包括目录名或指向一个可以从当前目录出发找到的文件

HTML超链接:<a href =“url”,target=””>

href 属性:链接地址 URL target 属性:目标的打开方式

锚点:文档中某行的一个记号,用于链接到文档中的某个位置

链接锚点:在锚点前加 # <a href =”#g1″>内容</a>

定义锚点:<a name=“g1”>内容</a>

俩者需对应使用,前后呼应

HTML表格

表格是由<table>标签来定义的,通常用来组织结构化信息,是被称作单元格的矩形框,按照从左到右,从上到下的顺序排列在一起而形成的,表格的数据保存在单元格里

​ 创建表格:<table></table>

​ 创建行:<tr></tr>

​ 创建列:<td></td>

表格常用的属性:

border​ 边框 

width/height​ 宽/高

​align​ 对齐方式

​padding​ 边框与文字之间的距离,内边距

cellspacing​:单元格之间的距离

​ 跨行属性:rowspan 合并行 colspan 合并列

行分组:表格看分为3个部分,分别为:表头,表主题,表尾

<table>
    <!--表头-->
    <thead>
    	<tr>
        	<th></th>
        </tr>
    </thead>
    <!--表主体-->
    <tbody>
    	<tr>
        	<td></td>
        </tr>
    </tbody>
    <!--表尾-->
    <tfoot>
    	<tr>
        	<td></td>
        </tr>
    </tfoot>
</table>

HTML表单:

表单是用于显示收集信息并提交信息到服务器,表单是一个包含表单元素的区域

俩要素:form元素,表单控件

表单是从浏览器向服务器传输数据的手段

表单元素:

定义表单使用成对的<form>标记,表示要将此元素中所涵盖的控件中的数据传入到服务器

主要属性:

​action​:表单提交URL;

method​:数据提交方式;

enctype​:表单数据进行编码的方式

表单控件:由许多不同类型的控件,其是一种HTML元素,是信息输入项,包含

input元素(具有不同的外观):文本框,密码框,单选框,按钮…

其他元素:标签,文本域,下拉选

input元素

​ 文本框 <input type = “text”/>

​ 密码框:<input type = “password”/>

​ 主要属性:value:由访问者自由输入任何文本

Maxlength​:限制输入的字符数

Readonly​:设置文本控件的只读

​ 单选框: <input type =“radio”/>

​ 复选框:<input type =“checkbox”/>

​ 属性:value:文本,当提交form时,选中单选按钮,则发送服务器

​ Name:实现分组,一组单选框或者复选框名称必须相同

​ Check:设置选中

提交按钮:<input type = “submit ” value=“提交”/>传送表单数据给服务器

重置按钮:<input type = “reset” value=“重置”/>清空内容,并 设为最初默认状态

普通按钮:<input type = “button” value=””/>执行客户端脚本

隐藏域:<input type = “hidden”/>表单中包含但不希望用户所见

文件选择框:<input type = “file”/>选择上传的文件

其他元素:

​ 标签:表单中的文本用于给控件设置显示名称

​ 语法:<label for=“控件ID”>文本</label>

​ 属性:for:设置该文本所关联的控件ID,关联后点击标签等同于点击控件

​ 文本域:多行文本框

​ 语法:<textrea></textrea>

​ 属性:cols:指定文本域的列数;rows:指定文本域的行数; readonly:该文本域只读

下拉选

语法:

<select>
	<option></option>
</select>

CSS概述

什么是CSS?

CSS是层叠样式表(Cascading Style Sheets),样式定义了如何显示HTML元素,样式通常储存在样式表中,CSS是HTML的化妆师

如何使用CSS?

内联样式:定义在单个HTML元素中

内部样式表 :定义在HTML的头元素中

外部样式表:将样式定义在外部的CSS文件中(.css),由HTML页面引用样式表文件

内联样式:定义在HTML元素的属性style里面

css语法:只需要将分号隔开的一个或多个属性作为元素的style属性值,属性值之间用冒号(:)连接,多个属性值之间用分号(;)隔开

内部样式表:在HTML的<head>元素内部添加<style>元素

外部样式表:样式定义在独立的CSS文件中,一个纯文本文件,后缀为.css,该文件只包含样式规则

使用方法:①创建外部样式表文件

②引用该样式文件

<link rel=”stylesheet” type=”text/css” href=”文件地址值”\>

CSS语法规范:

CSS规则由俩个部分构成:选择器,以及一条或多条样式声明

CSS注释:CSS注释以 “/*” 开始, 以 “*/” 结束,

​ /*这是个注释*/

CSS规则特性

优先级:同一个元素若存在多个CSS规则,对应冲突的声明以优先级高者为准(就近原则)

层叠性:同一个元素若存在多个CSS规则,对于不冲突的声明可以叠加

继承性:父元素的CSS声明可以被子元素继承,如字体,颜色

CSS选择器

元素选择器:能通过元素名来选择 CSS 作用的目标

类选择器:能够附带 class 属性的元素都可以使用此样式声明,将元素的 class 属性设置为样式类名,可以将类选择器和元素选择器结合使用,以实现对某种元素的中不同样式的细分控制 (.class_name)

ID选择器:以一种独立于文档元素的方式,它仅作用于id属性的值 (#id_name)

选择器组:选择器声明为以逗号隔开的选择器列表,将一些相同的规则作用于多个元素

派生选择器:用来选择子元素

分类:后代选择器:选择某元素的后代(子孙)元素

子元素选择器:选择某元素的所有子元素

伪类选择器:用于设置同一个元素在不同状态下的样式

常用伪类:

​:link​向未被访问的超链接添加样式

​:visited​ 向已被访问的添加样式

:active​:向未激活的元素添加样式

:hover​:当鼠标悬停到元素上方时,添加样式

​​:focus​:当元素获取焦点时,向该元素添加样式

border​:用来设置元素的边框

四边设置:border:width值 style值 color值

单边设置:border-left border-right border-top border-bottom

样式单位:%:百分比 in:英寸 cm:厘米

​ mm:毫米 pt 磅(1pt=1/72 in)

​ Px像素 1em 等于当前字体尺寸

​ Overflow​:当内容溢出元素边框时

​ Visible​ 不裁剪内容,可显示在内容框外

​ Hidden​ 裁剪内容,不提供滚动机制

​ Scroll​ 裁剪内容,提供滚动机制

​ Auto​ 如溢出,提供滚动

Box框模型:

元素框的最内部分是实际的内容,直接包围内容的是内边距。内边距呈现了元素的背景。内边距的边缘是边框。边框以外是外边距,外边距默认是透明的,因此不会遮挡其后的任何元素。

内边距、边框和外边距都是可选的,默认值是零。但是,许多元素将由用户代理样式表设置外边距和内边距。可以通过将元素的 margin 和 padding 设置为零来覆盖这些浏览器样式。这可以分别进行,也可以使用通用选择器对所有元素进行设置

  • Margin – 清除边框区域。Margin没有背景颜色,它是完全透明
  • Border – 边框周围的填充和内容。边框是受到盒子的背景颜色影响 `
  • Padding – 清除内容周围的区域。会受到框中填充的背景颜色影响
  • Content – 盒子的内容,显示文本和图像

背景色:

background-color:用于为元素设置背景色,可接受任何合法的颜色值

背景图片:

background-image​:设置背景图片,默认值为none,表示背景上没有放置任何图像,如需设置,则需要起始字符附带图像的url地址

默认情况下,背景图片是在水平和垂直方向上重复出现的

​ background-repeate​:可控制背景图片的平铺效果

​ ​repeate​:在水平和垂直方向重复

​ repeate-x​:在水平方向重复

​ repeate-y​:在垂直方向重复

​ no repeate​:仅显示一次

​ ​background-position​:用于改变背景图片在元素中的位置

CSS文本格式化

控制字体:

font-family:value 1,value 2 指定字体

font-size​:value 字体大小

font-weight​:normal/bold 字体加粗

color​:value 文本颜色

Text-align​:left/right/center 文本排列

Line-height​:value 行高

Text-indent​:value 首行文本缩进

表格样式:

常用属性:表格同样使用box模型(边框 ,内边距,宽,高)以及文本格式化属性

表格特有属性:如果设置了单元格边框,相邻单元格边框 会单独显示,类似于双线边框

border-collapse:合并相邻的边框,设置是否将表格的边框合并为一个边框

显示方式:元素都有自己默认的选择方式

块元素:从上到下显示,可以设置宽高 如:<P>,<div>,<h1>

行内元素:从左到右显示,不能设置宽高,如:<span>,<a>

行内块元素:从左到右显示,可以设置宽高,<input>,<img>

除了默认显示效果外,可以用display属性,修改元素的显示方式

具体修改方式:

​ display:none 表示不显示该元素,释放其占用的空间

​ display:block 表示将元素的显示方式设置为块

​ display:inline 表示将元素的显示方式设置为行内元素

​ display:inline-block:表示将元素的显示方式设置为行内块元素

定位:

定义元素框对于其正常位置应该出现的位置或相对于父元素另一个元素相对于浏览器窗口本身的位置

流定位:页面中的块级元素从上到下依次排列,每一个块级元素都会出现在新的一行,元素框之间的垂直距离,由框的垂直外边距计算得出

内联元素:在一行中从左到右,水平排列不需要换行,使用的是水平内边距,边框和外边距调整他们的间距

浮动定位:将元素排除在普通流之外,将浮动元素放置在包含框的左边或者右边,浮动元素依旧包含于框之外,浮动框可以向左或者向右移动,直到外边缘碰到包含框或者另一个浮动框位为止,如需要设置框浮动在包含框的左边或者右边,可以通过float属性实现具体方向的移动

任何元素都是可以移动的 float:none/left/right

clear清除浮动所带来的影响:clear:none/left/right/both

相对定位:元素原本所占的空间不释放,元素框会相对于原来的位置偏移某个距离,设置垂直或者水平,让元素相对于它的起点进行移动

首先需要设置position属性,其值为relative,然后使用left/right/top/bottom设置具体的偏移量

绝对定位:将元素的内容从当前定位中清除,释放空间,并使用偏移量来固定元素的位置,相对于最近的祖先元素,若偏移元素没有已定位的元素,那么它的位置就是相对于body元素的位置

​ 首先设置position属性,其值为absolute,然后使用left/right/top/bottom设置具体的偏移量

固定定位:将元素的内容固定在页面的某个位置,元素从普通流中完全移出,不占用页面空间,当用户将页面向下滚动时,元素看不随着移动

​ 首先设置position属性,其值为fixed,然后使用left/right/bottom/top设置具体的偏移量

堆叠顺序:一旦修改元素的定位方式,元素可能发生堆叠,可以使用z-index来控制有元素在框中出现的堆叠数值

​ Z-index:value 数值越大,级别越高,越显示在前

列表样式:

List-style-type:用于控制列表中列表项标志的一个样式

无序列表:出现在各列旁边的圆点

​ 无需列表的取值:none:无标记;disc:实心圆(默认);circle(空心圆);square 实心方块

有序列表:可能出现数字,字母或者其他用来排列计数

​ 有序列表的取值:none:无标记;decimal:数字;

​ lower-roman:小写罗马数字 upper-roman:大写罗马数字

​ list-style-image:使用图像替换列表项,取值为url

JavaScript

什么是javaScript?

嵌入在HTML中在浏览器中的脚本语言,具有与java和C语言类似的语言,一种网页的编程技术,用来向HTML页面添加交互行为,直接嵌入HTML页面,由浏览器解释执行代码,不进行预编译

​ 特点:可以使用任何文本工具编译,由浏览器内置的JavaScript引擎执行代码

​ 解析执行:事先不编译,逐行执行

​ 基于对象:内置大量线程对象

使用:客户端的数据计算,客户端表单合法性验证,浏览器事件触发,网页特殊显示效果制作,服务器的异常数据提交

JavaScript程序的用法:

事件定义式:在时间定义时,直接写JavaScript;

嵌入式:在使用Script标签

文件调用式:代码位于单独的(.js)文件中,html页面引用js文件,在script标签中添加文件的地址(src=””)

JavaScript代码错误:

解释性代码,代码错误则页面中无效果,可以打开网页的”检查””错误控制台”来查看错误

JavaScript语法规范:

基本语法:由Unicode字符集编写

注释:单行://注释内容 多行:/*注释内容*/

语句:表达式,关键字,运算符,大小写敏感,建议使用分号结尾一条语句

标识符和关键字:

标识符:不以数字开头的字母,数字,下划线和$组成

关键字:查看js手册

变量:使用关键字var声明变量,变量初始化使用”==”来赋值

没有初始化的变量自动取值为:undefined

变量无类型,统一使用var声明,变量所引用的数据有类型

JavaScript数据类型:

特殊类型:null: 空 undefined:未定义

内置对象:Number:数字 String:字符串 boolean:俩个值 true/false Array数组 function:函数

外部对象:window:浏览器对象 document:文档对象

自定义对象:Object:自定义对象

String类型:

​ 由Unicode字符,数字,标点符号组成的字符序列,直接量需要一对单/双引号括起

Number类型:

​ 在JavaScript中不区分整型数值和浮点型数值,整型直接量:默认的整数直接量为十进制

Boolean类型:

​ 仅有俩个值 true/false

数据类型转换:

​ ①数据类型之间隐式转换

​ ②转换函数:

·toString:所有数据类型均可以转换为String类型

·parseInt():强制转换为整数,如不能转换则出现NaN;

·parseFloat():强制转换为浮点数,不能转换会出现NaN;

·typeof:查看当前类型,返回String/Number/boolean/object/Function/undefined

·isNaN():判断被检测表达式转换后是否不是一个数,若不是数,则为true;否则为fasle

特殊数据类型:

​ Null:程序中无值/无对象,可以给一个变量赋值为null来清除内容

​ Undefined:声明变量,单位赋值/对象属性不存在

运算符:

算数运算:+,-,*,/,%,++(自增),–(自减)

关系运算:>,>=,<,<=,!=,==

​ ===:全等:类型相同,数值相同

​ !==:不全等

逻辑运算:与:&&;或:||;非(!)

条件运算:三目运算:表达式?表达式1:表达式2

控制语句:任何复杂的程序都可以通过顺序结构,分支结构,循环结构三种基本程序执行,默认结构为顺序结构

分支结构:if语句;switch-case与break联合使用

循环结构:for循环,while循环,do-while循环

JavaScript对象概述

对象是JavaScript中最重要的API,其中包含最多种对象,比如:内置对象,外部对象(window对象,dom对象),自定义对象

如何使用对象?

对象包含属性和函数,

访问对象的属性:对象.属性访问对象的方法: 对象.方法名

常见内置对象:

String对象:

创建对象:var str = “hello”; var str=new String (“hello”);

String对象的属性:length

常用方法:大小写转换:x.tolowerCase ; x.toUpperCase()

获取指定字符:x.charAt(index) 返回指定位置的字符

​ X.charCodeAt(index):返回指定位置的字符的Unicode编码

查询指定字符串:x.indexOf(findstr,[index]); x.lastindexOf(findstr,[index])

使用说明:findstr:查找的字符串;index:开始查找的位置索引,可以省略,如果没有找到则返回-1;lastindexOf:从后面开始找起

获取子字符串:x.substring(start,[end])

​ 使用说明:start:开始位置;end:结束位置

替换子字符串:X.replace(findstr,tostr)

​ 使用说明:findstr:要找的子字符串;tostr:替换的字符串

拆分字符串:x.split(bystr,[howmarny])

​ 使用说明:bystr分割用的字符串;howmarny返回的数组最大长度

Number对象:

Number对象是数值对象,创建方法为var num=123;

常用方法:toFixed(num)转换为字符串,并保留小数点后一定位数

Array对象:

创建数组对象:

var a1 = new Array();var a2 =new Array(6);
var a3 =new Array(100,"a",true);
var a4 = \[100 ,200,300\];

获取数组元素的个数:.length;

数组长度可变;

数组的倒序与排序:

X.reverse() 反向数组,改变数组X中数值的顺序

X.sort(sortfunc)数组排序:sortfunc:可选项,用来确定元素的函数名称

Math对象:

Math对象用于执行数学任务,无需创建,直接把math作为对象使用可以调用所有的属性和方法

三角函数:Math.sin(x),Math.COS(X),math.tan(x)

计算函数:Math.log(x)…

数值比较函数:

Date对象:

用于处理日期和时间,封装了系统毫秒数

创建方法:var now = new Date()

常用方法:读写时间毫秒数:getTime();setTime()

读写时间分量:getDate();getDay;setDate();setDay;toString…

RegExp对象

表示正则表达式 var rge = new RegExp();

常用方法:

​ x.replace(regexp,tostr)

​ x.match(regexp)

​ x.search(regexp)

​ exec(str)​检索字符串中指定的值,返回找到的值

​ test(str)​检索字符串中指定的值,返回 true 或 false

使用说明:

​ regexp​代表正则表达式或字符串

​ replace​返回替换后的结果

​ match​返回匹配字符串的数组

​ search​返回匹配字符串的首字符位置索引

Function对象

JS中函数就是Function对象,函数名就是指向Function对象的引用,使用函数名就可以访问函数对象

函数的返回值:默认返回undefined,可以使用return返回具体的值

函数的参数:JS的函数没有重载;调用时只要函数名一样,无论传入多少个参数,调用的都是同一个函数;没有接收的实参的参数值是undefined;所有的参数传递给arguments数组对象

Arguments:是一特殊的对象,在函数代码中,表示函数的参数数组,在函数代码中可以使用arguments访问所有参数

–arguments.length函数的参数个数

–arguments[i]:第i个参数

–可以使用arguments实现可变参数的函数

使用Function对象创建函数:

var abc = new Function("x","y","return(x+y)")
var result = abc(2,3);
Alert(result)//5
Alert(abc)//Function("x","y","return(x+y)")

匿名函数:

Var func = Function(x,y){return(x+y)}

Eval函数

​ Eval用于计算表达式字符串,或用于执行字符串中的JS代码

​ 只接收原始字符串作为参数,如果参数中没有合法的表达式和语句,抛出异常

​ var s1 = “2+3”; eval(s1) //5

外部对象概述

·BOM(Browser Object Model):

​ 浏览器对象模型,用来访问和操作浏览器窗口,是JavaScript有能力和浏览器”对话”,通过操作BOM,可以动窗口,更改状态栏文本,执行其他不与页面内容发生直接联系的操作

·DOM(Document Object Model)

​ 文档对象模型,用来操作文档,定义了访问和操作HTML文档的标准方法

·WINDOW对象:表示浏览器窗口

常用属性:

​ Document​:窗口中显示的HTML文档对象

​ ​History​:浏览器窗口的历史记录对象

​ ​Location​:窗口文件地址对象

​ ​Screen​:当前屏幕对象

​ ​Navigator​:浏览器相关信息

常用方法:

​ alert();confirm()

​ setTimeout();clearTimeout()

​ setInterval();clearInterval()

对话框:

​ alert(str)提示对话框 ,显示str字符串内容

​ confirm(str)确认对话框,显示str字符串内容,按”确定”按钮返回true,其他则返回false

定时器:

​ 多用于网页的动态时钟,制作倒计时,跑马灯效果等

​ 周期性时钟:以一定的间隔执行代码,循环往复

​ 一次性时钟:在一个设定的时间间隔之后执行代码,而不是在函数被调用后立即执行

周期性定时器:

​ setInterval(exp,time) exp:执行语句 time:时间间隔

​ clearInterval(tID)停止启动的定时器

一次性定时器:

​ setTimeout(exp,time)exp:执行语句 time:时间间隔,返回已经启动的定时器

​ clearTimeout(tID)停止启动的定时器

常用属性:

Screen​对象:包含有关客户端显示屏幕的信息,常用于获取屏幕的分辨率和色彩 Width/height/availwidth/availHeight

History​对象:包含用户访问过的URL

​ length​属性:浏览器历史记录列表中的URL数量

​ back()​:等同于后退按钮

​ forword()​:等同于前进按钮

​ go(num)​:等同于单机前后或后退num次

Location对象:

​ Location对象包含有关当前的URL信息,常用于获取或改变当前浏览的网址

​ href属性:当前窗口正在浏览器的网页地址

​ reload():重新载入当前网址,等同于刷新按钮

Navigator对象:

​ 包含有关浏览器的信息,常用于获取客户端浏览器和操作系统信息

DOM概述

DOM(document object model)文档对象模型

当网页被加载时,浏览器会创建页面的文档对象模型,通过可编程的对象模型。javaScript 获得了足够的能力来创建动态的 HTML,JavaScript可以改变页面中的所有 HTML 元素,属性,CSS 样式以及对页面中的所有事件做出反应

DOM节点树:DOM模型被构造为对象的数,这棵树的根就是 document 对象

DOM操作包括:查找节点,读取节点信息,修改节点信息,创建新节点,删除节点

读取,修改节点信息:

​ nodeName:节点名称

​ 元素节点和属性节点:标签或属性的名称

​ 文本节点:永远是text

​ 文档节点:永远的document

nodeType:节点类型

​ 返回数值:若是元素节点,返回1;属性节点:2;

​ 文本节点:3;注解节点:8;文档节点:9

元素节点的内容:

​ innerText​:设置或获取位于对象起始和结束标签内的文本

​​ innerHTML​:设置或获取位于对象起始和结束标签内的 HTML

节点属性:

​ ​getAttribute()​方法,根据属性名称获取属性的值

​ SetAttribute()​方法

​ RemoveAttribute()

​ 将HTML标记,属性和CSS都对象化

元素节点的样式:

​ style属性:node.style.color;node.style.fontsize

​ className属性:动态绑定样式

查询

查询节点:

​ 如果需要操作HTML元素,必须首先找到该元素,查询节点的方式有

  1. 通过id查询
  2. 通过层次(节点关系)查询
  3. 通过标签名称查询
  4. 通过name属性查询根据元素的id查询节点:document.getElementById();通过指定的id来返回元素节点,查询整个HTML文档中的任何HTML元素,如果id值错误,返回null

根据层次查询:

​ parentNode​:遵循文档的上下层次结构,查找单个父节点

​ childNodes​:遵顼文档的上下层次结构,查找多个子节点

根据标签名查询:

​ getElementByTagName()根据指定的标签名返回所有元素,查找整个HTML文档的所有元素,如果标签名错误,返回长度为0的节点列表

​ 若返回一个节点列表(数组),使用节点列表的length属性获取个数,[index]:定位具体的元素

根据name属性查询:

​ document.getElementByName():根据标签的name属性的值进行查询

增加

创建新节点:document.createElement(name) name:要创建元素的标签名称,返回新创建的节点

添加新节点:parentNode.appendChild(newNode)

newNode​:新节点作为父节点的最后一个子节点进行添加

parentNode.insertBefore(newNode,refNode) refNode是参考节点,新节点位于此节点之前添加

删除

删除节点: ​node.removeChild(childNode)​:删除某个子节点,childNode必须是 node 的子节点

事件

概述:指页面元素状态改变,用户在操作鼠标或者键盘时触发的动作,具体包括:鼠标事件,键盘事件,状态改变事件…

Event​: 事件触发后会产生一个 event 对象

事件属性:

鼠标事件:onclick 单击事件 ondblclick 双击事件

onmouseover​:鼠标移入事件 

onmouseout​:鼠标移出事件 

onmousedown​:鼠标点击事件 

onmouseup​:鼠标松开事件

event对象:

​ 任何事件触发后会产生一个 event 对象,event 对象记录事件发生时的鼠标位置,键盘按键状态和触发对象等信息

JQuery

JQuery 是一个优秀的 JavaScript 框架,一个轻量级的 JS 库,它封装了 JS,CSS,DOM 提供了一致的,简洁的 API,使用户更加方便的处理HTML,实现动画效果,并且方便为网站提供 Ajax 交互模型,使用户的 HTML 页面保持代码和 HTML 内容分离

使用JQuery

JQuery 的使用步骤:

  1. 引入 JQuery 的 js 文件
  2. 使用选择器定位操作节点
  3. 调用 JQuery 的方法进行操作什么是 JQuery 对象?JQuery 对象本质上是一个 DOM 对象数组,它在该数组上扩展了一些操作数组中元素的方法 Obj.length:获取数组的长度Obj.get(index):获取数组中的某一个 DOM 对象 Obj[index]:等价于obj.get(index)DOM对象转换为 JQuery 对象:$(DOM对象)

JQuery选择器:

JQuery选择器类似于CSS选择器(定位元素),能够实现定位元素,添加行为,使用JQuery选择器能够将内容与行为分离

选择器的分类:基本选择器,层次选择器,过滤选择器,表单选择器

基本选择器:

​ 元素选择器:根据标签来定位元素 $(“标签名”)

​ 类选择器:根据class属性定位元素 $(“.class属性名”)

​ Id选择器:根据id属性定位元素 $(“#id属性名”)

​ 选择器组:定位一组选择器所对应的所有元素 $(“#id,name”)

层次选择器:

​ 在select1元素下,选中所有满足select2的子孙(后代)元素 $(“select1 select2”)

​ 在select1元素下,选择所有满足select2的子元素

​ $(“select1>select2”)

过滤选择器:

根据元素的基本特征定位元素,常用于表格和列表

​​ first:​第一个元素;​last:​最后一个元素

​​ not(selector)​把 selector 排除在外

​​ even​ 挑选偶数行 ​odd ​挑选奇数行

​​ eq(index)​下标等于index元素

​ gt(index)​下标大于index的元素

​ lt(index)​下标小于index的元素

内容过滤选择器:

根据文本内容定位元素

​ ​contains(text)​匹配包含给定文本的元素

​ ​empty​ 匹配所有不包含子元素或文本的空元素

可见性过滤选择器:

​​ hidden​:匹配所有不可见元素

​​ visible​:匹配所有的可见元素

属性过滤选择器:

​ 根据属性定位元素

​ [attribute]匹配具有 attribute 属性的元素

状态过滤选择器:

​ 根据状态定位元素

表单选择器:

​ 包括:text:匹配文本框 password:匹配密码框…

读写节点:

读写节点的HTML内容:

​ 读入:obj.html() 写出:obj.html(“写出内容”)

读写节点的文本内容:

​ 读入:obj.text() 写出:obj.text(“写出内容”)

读写节点的value属性值 :

​ 读入:obj.val() 写出:obj.val(“写出内容”)

读写节点的属性值:

​ 读入:obj.attr(“属性名”) 写出:obj.attr(“属性名”,“属性值”)

增删节点:

创建DOM节点:$(’‘节点内容’’)

插入DOM节点:

​ ​parent.append(obj)​ 作为最后一个子节点添加

​ ​parent.prepend(obj)​ 作为第一个子节点添加

删除DOM节点:

​ ​Obj.remove()​ 删除节点

​ ​Obj.remove(selector)​ 只删除满足 selector 的节点

​ ​Obj.empty() ​清空节点

样式:

​ ​addClass(" ")​追加样式

​ ​removeClass(" ")​移出指定样式

​ ​removeClass() ​移出所有样式

​ ​toggleClass(" ")​切换样式

​ ​hasClass("")​判断是否有这个样式

​ ​css("")​读取css的值

​ ​css("","")​设置多个样式

遍历节点:

children()​取得一个包含匹配的元素集合中的每一个元素的所有子元素的元素集合

parent()​ 父节点

事件处理:参考手册

等待页面加载完毕后执行:$(function(){…})

获得事件对象 event

只需要对事件处理函数传递一个参数 如:$obj.click(function(e){…}); e 就是事件对象,已经经过了 JQuery 的底层事件对象的封装,封装后的事件对象可以方便的兼容各浏览器

事件的常用属性:

​ 获取事件源:e.target 返回值就是DOM对象

:匹配所有的可见元素

属性过滤选择器:

​ 根据属性定位元素

​ [attribute]匹配具有 attribute 属性的元素

状态过滤选择器:

​ 根据状态定位元素

表单选择器:

包括:

text:匹配文本框 

password:匹配密码框…

读写节点:

读写节点的HTML内容:

​ 读入:obj.html() 写出:obj.html(“写出内容”)

读写节点的文本内容:

​ 读入:obj.text() 写出:obj.text(“写出内容”)

读写节点的value属性值 :

​ 读入:obj.val() 写出:obj.val(“写出内容”)

读写节点的属性值:

​ 读入:obj.attr(“属性名”) 写出:obj.attr(“属性名”,“属性值”)

增删节点:

创建DOM节点:$("节点内容")

插入DOM节点:

​parent.append(obj) ​作为最后一个子节点添加

​​ parent.prepend(obj)​ 作为第一个子节点添加

删除DOM节点:

​ ​Obj.remove()​ 删除节点

​ ​Obj.remove(selector)​ 只删除满足 selector 的节点

​ ​Obj.empty()​ 清空节点

样式:

​ addClass(" ")​追加样式

​ ​removeClass(" ")​移出指定样式

​ ​removeClass()​ 移出所有样式

​​ toggleClass(" ")​切换样式

​ ​hasClass("")​判断是否有这个样式

​ ​css("")​读取css的值

​ css("","")​设置多个样式

遍历节点:

children()​取得一个包含匹配的元素集合中的每一个元素的所有子元素的元素集合

parent()​ 父节点

事件处理:参考手册

等待页面加载完毕后执行:$(function(){…})

获得事件对象event

只需要对事件处理函数传递一个参数 如:$obj.click(function(e){…}); e 就是事件对象,已经经过了JQuery 的底层事件对象的封装,封装后的事件对象可以方便的兼容各浏览器

事件的常用属性:

​ 获取事件源:e.target 返回值就是DOM对象

​ 获取鼠标点击的坐标 e.pageX e.pageY

python的5种高级用法,效率提高没毛病!

thbcm阅读(163)

任何编程语言的高级特征通常都是通过大量的使用经验才发现的。比如你在编写一个复杂的项目,并在 stackoverflow 上寻找某个问题的答案。然后你突然发现了一个非常优雅的解决方案,它使用了你从不知道的 Python 功能!

这种学习方式太有趣了:通过探索,偶然发现什么。

下面是 Python 的 5 种高级特征,以及它们的用法。

前言彩蛋

Lambda 函数

Lambda 函数是一种比较小的匿名函数——匿名是指它实际上没有函数名。

Python 函数通常使用 def a_function_name() 样式来定义,但对于 lambda 函数,我们根本没为它命名。这是因为 lambda 函数的功能是执行某种简单的表达式或运算,而无需完全定义函数。

lambda 函数可以使用任意数量的参数,但表达式只能有一个。

看它多么简单!我们执行了一些简单的数学运算,而无需定义整个函数。这是 Python 的众多特征之一,这些特征使它成为一种干净、简单的编程语言。

Map 函数

Map() 是一种内置的 Python 函数,它可以将函数应用于各种数据结构中的元素,如列表或字典。对于这种运算来说,这是一种非常干净而且可读的执行方式。

Filter 函数

filter 内置函数与 map 函数非常相似,它也将函数应用于序列结构(列表、元组、字典)。二者的关键区别在于 filter() 将只返回应用函数返回 True 的元素。

详情请看如下示例

我们不仅评估了每个列表元素的 True 或 False,filter() 函数还确保只返回匹配为 True 的元素。非常便于处理检查表达式和构建返回列表这两步。

Itertools 模块

Python 的 Itertools 模块是处理迭代器的工具集合。迭代器是一种可以在 for 循环语句(包括列表、元组和字典)中使用的数据类型。

使用 Itertools 模块中的函数让你可以执行很多迭代器操作,这些操作通常需要多行函数和复杂的列表理解。关于 Itertools 的神奇之处,请看以下示例:

Generator 函数

Generator 函数是一个类似迭代器的函数,即它也可以用在 for 循环语句中。这大大简化了你的代码,而且相比简单的 for 循环,它节省了很多内存。

比如,我们想把 1 到 1000 的所有数字相加,以下代码块的第一部分向你展示了如何使用 for 循环来进行这一计算。

如果列表很小,比如 1000 行,计算所需的内存还行。但如果列表巨长,比如十亿浮点数,这样做就会出现问题了。使用这种 for 循环,内存中将出现大量列表,但不是每个人都有无限的 RAM 来存储这么多东西的。Python 中的 range() 函数也是这么干的,它在内存中构建列表。

代码中第二部分展示了使用 Python generator 函数对数字列表求和。generator 函数创建元素,并只在必要时将其存储在内存中,即一次一个。这意味着,如果你要创建十亿浮点数,你只能一次一个地把它们存储在内存中!Python 2.x 中的 xrange() 函数就是使用 generator 来构建列表。

上述例子说明:如果你想为一个很大的范围生成列表,那么就需要使用 generator 函数。如果你的内存有限,比如使用移动设备或边缘计算,使用这一方法尤其重要。

也就是说,如果你想对列表进行多次迭代,并且它足够小,可以放进内存,那最好使用 for 循环或 Python 2.x 中的 range 函数。因为 generator 函数和 xrange 函数将会在你每次访问它们时生成新的列表值,而 Python 2.x range 函数是静态的列表,而且整数已经置于内存中,以便快速访问。

推荐好课:Python3入门Python3进阶Python静态爬虫

随着AI人工智能计算机做出更多决策,人们对偏颇算法的担忧也与日俱增

thbcm阅读(170)

来源:MSN

作者:Shara Tibken

翻译:W3Cschool

去年年底美国开始分发 COVID-19 疫苗时,出现了一个重要问题:谁应该优先注射疫苗?许多医疗机构和卫生官员决定优先给与感染者密切接触的工作人员接种疫苗,包括医护、安保人员。斯坦福大学做为该国最顶尖大学之一,建立了一种确定顺序的算法。

让计算机决定谁先接种疫苗的唯一问题是它的“非常复杂的算法”(事实证明根本不是很复杂)是建立在错误的假设和数据之上的。也就是说,该算法优先考虑特定年龄的医务人员,而不考虑许多年长的医生没有定期看病人的情况。斯坦福大学(Stanford Medicine)首批 COVID-19 疫苗的 5,000 剂疫苗中,只有七剂被分配一线驻地医生。绝大多数被分配到了在家工作或与感染 COVID-19 的患者接触很少的高级教师和医生。斯坦福大学迅速取消了该算法,并为一线员工接种了疫苗。

斯坦福大学门诊护理团队主任蒂姆·莫里森(Tim Morrison)12月中旬在发布的 Twitter 视频中说:“我们的算法,伦理学家和传染病专家工作了几个星期,使用年龄、高风险的工作环境以及该环境中的阳性率… … 等条件中,显然没有正确的工作”

斯坦福大学的疫苗崩溃只是算法有偏见的许多方式中的一个例子,随着计算机程序取代人类决策者,这个问题变得越来越明显。算法有望在没有情感影响的情况下根据数据做出决策:可以更快、更公平、更准确地做出决策。然而,算法并不总是基于理想的数据,这一缺点在进行生死攸关的决定(例如重要疫苗的分配)时会被放大。

根据加利福尼亚州奥克兰市一家致力于种族和经济正义的非营利性机构 Greenlining Institute 周二发布的一份报告显示:其影响甚至更为广泛,因为计算机可以确定某人是否获得房屋贷款,谁被雇用以及囚犯被关押的时间。Greenlining 首席执行官黛布拉·戈尔·曼(Debra Gore-Mann)表示,算法通常会保留与人类决策者相同的种族,性别和收入水平偏差。

戈尔·曼在接受采访时说:“您正在看到这些工具被用于刑事司法评估、住房评估、金融信贷、教育、求职。它变得如此普遍,以至于我们大多数人甚至都不知道正在进行某种自动化和数据评估。”

Greenlining 报告研究了设计不当的算法如何威胁到加剧系统性种族主义、性别歧视和对低收入人群的偏见。由于这项技术是由人们创造和培训的,因此这些算法(无论是否有意)都可以重现歧视和偏见的模式,而人们通常不会意识到这种情况的发生。面部识别是被证明存在种族偏见的技术领域之一。健身带一直在努力准确地测量有色人种的心率。

Greenlining 技术股权法律顾问 Vinhcent Le 说:“用于超目标全球广告的相同技术也正用于向人们收取不同价格的产品,这些产品对于经济健康至关重要,例如抵押产品保险,以及不太重要的产品(例如鞋),” 。

在另一个示例中,Greenlining 标记了由 Optum Health 创建的算法,该算法可用于确定患者的医疗照顾优先级。其中一个因素是病人在医疗费用上的花费,假设病情最严重的人在医疗保健上花费最多。仅仅使用这个参数不会考虑到那些没有那么多钱的人有时不得不在支付房租和支付医疗账单之间做出选择,这会不成比例地伤害到黑人患者。

Optum Health 表示,健康服务提供者以这种方式测试了算法的使用,但最终没有使用它来确定医疗服务。

Optum 在一份声明中说:“这个算法没有种族偏见。这个工具的设计目的是根据个别病人过去的医疗经验来预测未来可能产生的费用,并且在用于此目的时不会导致种族偏见——研究作者同意这一事实。”

没有简单的解决方法

Greenlining 为政府和公司提供了三种方法来确保技术更好。Greenlining 建议组织实行算法透明度和问责制;在有意义的情况下开发种族意识算法,并特别寻求将弱势群体纳入算法假设。

确保这种情况发生的责任,将落在立法者身上。

勒(Le)说:“(这份报告)的重要意义是树立起开始监管人工智能的政治意愿。”

在加利福尼亚州,州议会正在考虑第 13 号议会法案,也被称为 2021 年自动决策系统问责法案。它于 12 月 7 日推出,由 Greenlining 赞助,它将要求使用“自动决策系统“的企业测试其偏见及其对边缘群体的影响。如果有影响,组织必须解释,为什么歧视待遇不违法。勒说:“你可以区别对待别人,但如果是基于受保护的特征,比如种族、性别和年龄,那就是违法的。”

2019 年 4 月,民主党人新泽西州参议员科里 · 布克(Cory Booker)和俄勒冈州参议员罗恩 · 怀登(Ron Wyden),以及纽约州众议员伊维特·D·克拉克(Yvette D. Clarke)。提出了算法问责法案,该法案要求公司研究和修正有缺陷的计算机算法,这些算法导致了不准确、不公平、偏见或歧视性的决定。一个月后,新泽西州引入了类似的算法责任法案。两个法案都没通过委员会。

勒说,如果加利福尼亚州的 AB13 通过,那将是美国第一个这样的法律,但是它可能会失败,因为它的范围太广了,因为它目前是书面的。相反,Greenlining 希望将法案的职责范围缩小到首先关注政府创建的算法。希望该法案将为国家努力树立榜样。

勒说:“算法的大多数问题并不是因为人们对目标有偏见。他们只是在开发这些程序时走捷径。就斯坦福疫苗计划而言,算法开发人员没有考虑到后果。 ”

Le补充说:“没有人真的非常确定需要改变的所有事情。但是(我们)确实知道,当前的系统不能很好地处理AI。”

javascript设计模式:职责链模式

thbcm阅读(170)

职责链的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象能处理它为止,传递链中的这些对象就叫节点。

需求背景: 一个电商网站,用户交500定金且定金已付时,可享受500优惠券且不受货物数量限制;用户交200定金且定金已付时,可享受500优惠券且不受货物数量限制;用户不交定金时受货物数量限制,有货时原价买,无货时则无法买。

原始版本, ​if else​一路判断

var buyOrder = function(orederType, pay, stock){
    if(orederType == 1){
        if(pay){
            console.log('500优惠券');
        }else {
            if(stock > 0){
                console.log('普通购物页面');
            }else {
                console.log('已无货');
            }
        }
    }else if(orederType == 2){
        if(pay){
            console.log('200优惠券');
        }else {
            if(stock > 0){
                console.log('普通购物页面');
            }else {
                console.log('已无货');
            }
        }
    }else if(orederType == 3){
        if(stock > 0){
            console.log('普通购物页面');
        }else {
            console.log('已无货');
        }
    }
}

buyOrder(1, true, 600)

改进版本

var order500 = function(orderType, pay , stock){
    if(orderType == '1' && pay == true){
        console.log('500优惠券');
    }else {
        order200(orderType, pay , stock)
    }
}

var order200 = function(orderType, pay , stock){
    if(orderType == '2' && pay == true){
        console.log('200优惠券');
    }else {
        orderNormal(orderType, pay , stock)
    }
}

var orderNormal = function(orderType, pay , stock){
    if(stock > 0){
        console.log('普通购物页面');
    }else {
        console.log('已无货');
    }
}

order500(3, true, 0)

优化版本1:
同步的职责链

//3个订单函数 ,它们都是节点函数
var order500 = function(orderType, pay , stock){
    if(orderType == '1' && pay == true){
        console.log('500优惠券');
    }else {
        return 'nextSuccessor';     //我不知道下个节点是谁,反正把请求往后传递
    }
}

var order200 = function(orderType, pay , stock){
    if(orderType == '2' && pay == true){
        console.log('200优惠券');
    }else {
        return 'nextSuccessor';     //我不知道下个节点是谁,反正把请求往后传递
    }
}

var orderNormal = function(orderType, pay , stock){
    if(stock > 0){
        console.log('普通购物页面');
    }else {
        console.log('已无货');
    }
}

//职责构造函数
var Chain = function(fn){
    this.fn = fn;
    this.successor = null;
}

Chain.prototype.setNextSuccessor = function(successor){     //设置职责顺序方法
    this.successor = successor
}

Chain.prototype.passRequest = function(){       //请求传递
    var ret = this.fn.apply(this, arguments)

    if(ret === 'nextSuccessor'){
        return this.successor && this.successor.passRequest.apply(this.successor, arguments)
    }

    return ret;
}

//把3个订单函数分别包装成职责链的节点
var chainOrder500 = new Chain(order500)
var chainOrder200 = new Chain(order200)
var chainOrderNormal = new Chain(orderNormal)

//然后指定节点在职责链中的顺序
chainOrder500.setNextSuccessor(chainOrder200)
chainOrder200.setNextSuccessor(chainOrderNormal)

//最后把请求传递给第一个节点,开启职责链模式传递
chainOrder500.passRequest(1, true, 500)     //500优惠券
chainOrder500.passRequest(3, true, 20)      //普通购物页面
chainOrder500.passRequest(3, true, 0)       //已无货

//此时如果中间有需求改动,只需如此做: 
var order300 = function(){
    if(orderType == '3' && pay == true){
        console.log('300优惠券');
    }else {
        return 'nextSuccessor';     //我不知道下个节点是谁,反正把请求往后传递
    }
}
var chainOrder300 = new Chain(order300)     //添加新职责节点
chainOrder500.setNextSuccessor(chainOrder300)
chainOrder300.setNextSuccessor(chainOrder300)   //修改职责链顺序
chainOrder200.setNextSuccessor(chainOrderNormal)

//这样,就可以完全不必去理会原来的订单函数代码,只需增加一个节点,然后重新设置职责链中的相关节点的顺序就行。

优化版本2:异步的职责链

在实际开发中,经常会遇到 一些异步的问题,比如要在节点函数中发起一个ajax请求,异步请求返回的结果才能决定是否继续在职责链中passRequest

可以给Chain类再增加一个原型方法:

//职责构造函数
var Chain = function(fn){
    this.fn = fn;
    this.successor = null;
}

Chain.prototype.setNextSuccessor = function(successor){     //设置职责顺序方法
    this.successor = successor
}

Chain.prototype.passRequest = function(){       //请求传递
    var ret = this.fn.apply(this, arguments)

    if(ret === 'nextSuccessor'){    //传递给职责链中的下一个节点
        return this.successor && this.successor.passRequest.apply(this.successor, arguments)
    }

    return ret;
}

//新增,表示手动传递请求给职责链中的下一个节点
Chain.prototype.next = function(){
    return this.successor && this.successor.passRequest.apply(this.successor, arguments)
}


//异步职责链例子
var fn1 = new Chain(function(){
    console.log(1);
    return 'nextSuccessor'
})

var fn2 = new Chain(function(){
    console.log(2);
    var self = this;
    setTimeout(function(){
        self.next()
    }, 1000)
})

var fn3 = new Chain(function(){
    console.log(3);
})


//指定节点在职责链中的顺序
fn1.setNextSuccessor(fn2)
fn2.setNextSuccessor(fn3)

//把请求传递给第一个节点,开始节点传递
fn1.passRequest()

//输出 1 2 ...(1秒后)... 3

//这是一个异步职责链,请求在职责链节点中传递,但节点有权利决定什么时候 把请求交给下一个节点。这样可以创建一个异步ajax队列库。 

tips:

这里补充个知识点:“短路求值” ​&&​ 会返回第一个假值​(0, null, "", undefined, NaN)​,而​ || ​则会返回第一个真值。

var x = a || b || c​ 等价于:

var x;
if(a){
    x = a;
} else if(b){
    x = b;
} else {
    x = c;
}

var x = a && b && c 等价于:

var x = a;
if(a){
    x = b;
    if(b){
        x = c;
    }
}

所以 ​&&​ 有时候会用来代替 ​if (expression) doSomething()​,转成 ​&&​方式就是 ​expression && doSomething()​。

而 ​||​ 比较用来在函数中设置默认值,比如:

function doSomething(arg1, arg2, arg3) {
    arg1 = arg1 || 'arg1Value';
    arg2 = arg2 || 'arg2Value';
}

不过还需要看具体的使用场景,就比如如果要求 ​doSomething()​ 传入的 arg1 为一个数值,则上面的写法就会出现问题(在传入 0 的时候被认为是一个假值而使用默认值)。

现在个人比较常用的方法只判断是否与 ​undefined​ 相等,比如

function doSomething(arg) {
    arg = arg !== void 0 ? arg : 0;
}

职责链模式的优势:解耦请求发送者和 N 个接收者之间的复杂关系,由于不知道链条中的哪个节点可以处理你发出的请求,所以只需把请求传递给第一个节点就行。

如果在实际开发中,当维护一个含有多个条件分支语句的巨大函数时时,可以使用职责链模式。链中的节点对象可以灵活拆分重组,增加删除节点,且无需改动其他节点函数内的代码。

联系我们