当前位置: 首页 > 编程日记 > 正文

「前端面试题系列7」Javascript 中的事件机制(从原生到框架)

图片描述

前言

这是前端面试题系列的第 7 篇,你可能错过了前面的篇章,可以在这里找到:

  • 理解函数的柯里化
  • ES6 中箭头函数的用法
  • this 的原理以及用法
  • 伪类与伪元素的区别及实战
  • 如何实现一个圣杯布局?
  • 今日头条 面试题和思路解析

最近,小伙伴L 在温习 《JavaScript高级程序设计》中的 事件 这一章节时,产生了困惑。

他问了我这样几个问题:

  • 了解事件流的顺序,对日常的工作有什么帮助么?
  • 在 vue 的文档中,有一个修饰符 native ,把它用 . 的形式 连结在事件之后,就可以监听原生事件了。它的背后有什么原理?
  • 事件的 event 对象中,有好多的属性和方法,该如何使用?

浏览器中的事件机制,也经常在面试中被提及。所以这回,我们共同探讨了这些问题,并最终整理成文,希望帮到有需要的同学。

事件流的概念

先从概念说起,DOM 事件流分为三个阶段:捕获阶段目标阶段冒泡阶段。先调用捕获阶段的处理函数,其次调用目标阶段的处理函数,最后调用冒泡阶段的处理函数。

网景公司提出了 事件捕获 的事件流。这就好比采矿的小游戏,每次都会从地面开始一路往下,抛出抓斗,捕获矿石。在上图中就是,某个 div 元素触发了某个事件,最先得到通知的是 window,然后是 document,依次往下,直到真正触发事件的那个目标元素 div 为止。

事件冒泡 则是由微软提出的,与之顺序相反。还是刚才的采矿小游戏,命中目标后,抓斗再沿路收回,直到冒出地面。在上图中就是,事件会从目标元素 div 开始依次往上,直到 window 对象为止。

w3c 为了制定统一的标准,采取了折中的方式:先捕获在冒泡。同一个 DOM 元素可以注册多个同类型的事件,通过 addEventListener 和 removeEventListener 进行管理。addEventListener 的第三个参数,就是为了捕获和冒泡准备的。

注册事件(addEventListener) 有三个参数,分别为:"事件名称", "事件回调", "捕获/冒泡"(布尔型,true代表捕获事件,false代表冒泡事件)。

target.addEventListener(type, listener[, useCapture]);
  • type 表示事件类型的字符串。
  • listener 是一个实现了 EventListener 接口的对象,或者是一个函数。当所监听的事件类型触发时,会接收到一个事件通知对象(实现了 Event 接口的对象)。
  • capture 表示 listener 会在该类型的事件捕获阶段,传播到该 EventTarget 时触发,它是一个 Boolean 值。

解除事件(removeEventListener) 也有三个参数,分别为:"事件名称", "事件回调", "捕获/冒泡"(Boolean 值,这个必须和注册事件时的类型一致)。

target.removeEventListener(type, listener[, useCapture]);

要想注册过的事件能够被解除,必须将回调函数保存起来,否则无法解除。例如这样:

const btn = document.getElementById("test");//将回调存储在变量中
const fn = function(e){alert("ok");
};//绑定
btn.addEventListener("click", fn, false);//解除
btn.removeEventListener("click", fn, false);

事件捕获和冒泡的5个注意点

当有多层交互嵌套时,事件捕获和冒泡的先后顺序,似乎不是那么好理解。接下来,将分 5 种情况讨论它们的顺序,以及如何规避意外情况的发生。

1.在外层 div 注册事件,点击内层 div 来触发事件时,捕获事件总是要比冒泡事件先触发(与代码顺序无关)

假设,有这样的 html 结构:

<div id="test" class="test"><div id="testInner" class="test-inner"></div>
</div>

然后,我们在外层 div 上注册两个 click 事件,分别是捕获事件和冒泡事件,代码如下:

const btn = document.getElementById("test");//捕获事件
btn.addEventListener("click", function(e){alert("capture is ok");
}, true);//冒泡事件
btn.addEventListener("click", function(e){alert("bubble is ok");
}, false);

点击内层的 div,先弹出 capture is ok,后弹出 bubble is ok。只有当真正触发事件的 DOM 元素是内层的时候,外层 DOM 元素才有机会模拟捕获事件和冒泡事件。

2.当在触发事件的 DOM 元素上注册事件时,哪个先注册,就先执行哪个

html 结构同上,js 代码如下:

const btnInner = document.getElementById("testInner");//冒泡事件
btnInner.addEventListener("click", function(e){alert("bubble is ok");
}, false);//捕获事件
btnInner.addEventListener("click", function(e){alert("capture is ok");
}, true);

本例中,冒泡事件先注册,所以先执行。所以,点击内层 div,先弹出 bubble is ok,再弹出 capture is ok

3.当外层 div 和内层 div 同时注册了捕获事件时,点击内层 div 时,外层 div 的事件一定会先触发

js 代码如下:

const btn = document.getElementById("test");
const btnInner = document.getElementById("testInner");btnInner.addEventListener("click", function(e){alert("inner capture is ok");
}, true);btn.addEventListener("click", function(e){alert("outer capture is ok");
}, true);

虽然外层 div 的事件注册在后面,但会先触发。所以,结果是先弹出 outer capture is ok,再弹出 inner capture is ok

4.同理,当外层 div 和内层 div 都同时注册了冒泡事件,点击内层 div 时,一定是内层 div 事件先触发。

const btn = document.getElementById("test");
const btnInner = document.getElementById("testInner");btn.addEventListener("click", function(e){alert("outer bubble is ok");
}, false);btnInner.addEventListener("click", function(e){alert("inner bubble is ok");
}, false);

先弹出 inner bubble is ok,再弹出 outer bubble is ok

5.阻止事件的派发

通常情况下,我们都希望点击某个 div 时,就只触发自己的事件回调。比如,明明点击的是内层 div,但是外层 div 的事件也触发了,这是就不是我们想要的了。这时,就需要阻止事件的派发。

事件触发时,会默认传入一个 event 对象,这个 event 对象上有一个方法:stopPropagation。MDN 上的解释是:阻止 捕获 和 冒泡 阶段中,当前事件的进一步传播。所以,通过此方法,让外层 div 接收不到事件,自然也就不会触发了。

btnInner.addEventListener("click", function(e){//阻止冒泡e.stopPropagation();alert("inner bubble is ok");
}, false);

事件代理

我们经常会遇到,要监听列表中多项 li 的情况,假设我们有一个列表如下:

<ul id="list"><li id="item1">item1</li><li id="item2">item2</li><li id="item3">item3</li><li id="item4">item4</li>
</ul>

如果我们要实现以下功能:当鼠标点击某一 li 时,输出该 li 的内容,我们通常的写法是这样的:

window.onload=function(){const ulNode = document.getElementById("list");const liNodes = ulNode.children;for(var i=0; i<liNodes.length; i++){liNodes[i].addEventListener('click',function(e){console.log(e.target.innerHTML);}, false);}
}

在传统的事件处理中,我们可能会按照需要,为每一个元素添加或者删除事件处理器。然而,事件处理器将有可能导致内存泄露,或者性能下降,用得越多这种风险就越大。JavaScript 的事件代理,则是一种简单的技巧。

用法及原理

事件代理,用到了在 JavaSciprt 事件中的两个特性:事件冒泡 和 目标元素。使用事件代理,我们可以把事件处理器添加到一个元素上,等待一个事件从它的子级元素里冒泡上来,并且可以得知这个事件是从哪个元素开始的。

改进后的 js 代码如下:

window.onload=function(){const ulNode=document.getElementById("list");ulNode.addEventListener('click', function(e) {/*判断目标事件是否为li*/if(e.target && e.target.nodeName.toUpperCase()=="LI"){console.log(e.target.innerHTML);}}, false);
};

一些常用技巧

回到文章开头的问题:了解事件流的顺序,对日常的工作有什么帮助呢?我总结了以下几个注意点。

1. 阻止默认事件

比如 href 的链接跳转,submit 的表单提交等。可以在方法的最后,加上一行 return false;。它会阻止通过 on 的方式绑定的事件的默认事件。

ele.onclick = function() {……// 通过返回 false 值,阻止默认事件行为return false;
}

另外,重写 onclick 会覆盖之前的属性,所以解绑事件可以这么写:

// 解绑事件,将 onlick 属性设为 null 即可
ele.onclick = null;

2. stopPropagation 和 stopImmediatePropagation

前面说过 stopPropagation 的定义是:终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。事件不再被分派到其他节点上。

// 事件捕获到 ele 元素后,就不再向下传播了
ele.addEventListener('click', function (event) {event.stopPropagation();
}, true);// 事件冒泡到 ele 元素后,就不再向上传播了
ele.addEventListener('click', function (event) {event.stopPropagation();
}, false);

但是,stopPropagation 只会阻止当前元素 同类型的 事件冒泡或捕获的传播,并不会阻止该元素上 其他类型 事件的监听。以 click 事件为例:

ele.addEventListener('click', function (event) {event.stopPropagation();console.log(1);
});ele.addEventListener('click', function(event) {// 仍然可以触发console.log(2);
});

如果想禁用之后所有的 click 事件,就要用到 stopImmediatePropagation 了。但是,需要注意的是,stopImmediatePropagation 只会禁用之后注册的同类型的监听事件。就比如阻止了之后的 click 事件监听函数,但别的事件类型如 mousedown、dblclick 之类,还是可以监听到的。

ele.addEventListener('click', function (event) {event.stopImmediatePropagation();console.log(1);
});ele.addEventListener('click', function(event) {// 不会触发console.log(2);
});ele.addEventListener('mousedown', function(event) {// 会触发console.log(3);
});

3. jquery 中的 return false;

jquery 中的 on 是事件冒泡。当用 return false; 阻止浏览器的默认行为时,会做下面这 3 件事:

  • event.preventDefault();
  • event.stopPropagation();
  • 停止回调函数执行并立即返回。

这 3 件事中,只有 preventDefault 是用来阻止默认行为的。除非你还想阻止事件冒泡,否则直接用 return false; 会埋下隐患。

4. angular 中的 $event

angular 是个包罗万象的框架,似乎学完它的一整套之后,就能玩转世界了。它加工封装了许多原生的东西,其中就包括了 event,只是前面需要加一个 $,表示这是 angular 中的特有对象。

// template
<div><button (click)="doSomething($event)">Click me</button>
</div>// js
doSomething($event: Event) {$event.stopPropagation();...
}

$event 在这里作为一个变量,显式地 传入回调函数,之后就可以将 $event 当做原生的事件对象来用了。

5. vue 中的 native 修饰符

在 vue 的自定义组件中绑定原生事件,需要用到修饰符 native。

那是因为,我们的自定义组件,最终会渲染成原生的 html 标签,而非类似于 这样的自定义组件。如果想让一个普通的 html 标签触发事件,那就需要对它做事件监听(addEventListener)。修饰符 native 的作用就在这里,它可以在背后帮我们绑定了原生事件,进行监听。

一个常用的场景是,配合 element-ui 做登录界面时,输完账号密码,想按一下回车就能登录。就可以像下面这样用修饰符:

<el-inputclass="input"v-model="password" type="password"@keyup.enter.native="handleSubmit">
</el-input>

el-input 就是自定义组件,而 keyup 就是原生事件,需要用 native 修饰符进行绑定才能监听到。

6. react 中的合成事件

想要在 react 的事件回调中使用 event 对象,会产生困扰,会发现不少原生的属性都是 null。

那是因为在 react 中的事件,其实是合成事件(SyntheticEvent),并不是浏览器的原生事件,但它也符合 w3c 规范。

举一个简单的例子,我们要实现一个组件,它有一个按钮,点击按钮后会显示一张图片,点击这张图片之外的任意区域,可以隐藏这张图片,但是点击该图片本身时,不会隐藏。代码如下:

class ShowImg extends Component {constructor(props) {super(props);this.state = {active: false};}componentDidMount() {document.addEventListener('click', this.hideImg.bind(this));}componentWillUnmount() {document.removeEventListener('click', this.hideImg);}hideImg () {this.setState({ active: false });}handleClickBtn() {this.setState({ active: !this.state.active });}handleClickImg (e) {e.stopPropagation();}render() {return (<div className="img-wrapper"><buttonclassName="showImgBtn"onClick={this.handleClickBtn.bind(this)}>显示图片</button><divclassName="img"style={{ display: this.state.active ? 'block' : 'none' }}onClick={this.handleClickImg.bind(this)}><img src="@/assets/avatar.jpg" ></div></div>);}
}

按照之前说的原生事件机制,我们会错误地认为通过:

handleClickImg (e) {e.stopPropagation();
}

就可以阻止事件的派发了,但其实没法这么做。想要解决这个问题,当然也不复杂,就把 react 的事件和原生事件分开即可。

componentDidMount() {document.addEventListener('click', this.hideImg.bind(this));document.addEventListener('click', this.imgStopPropagation.bind(this));
}componentWillUnmount() {document.removeEventListener('click', this.hideImg);document.removeEventListener('click', this.imgStopPropagation);
}hideImg () {this.setState({ active: false });
}imgStopPropagation (e) {e.stopPropagation();
}

7. 事件对象 event

当对一个元素进行事件监听的时候,它的回调函数里就会默认传递一个参数 event,它是一个对象,包含了许多属性。我列出了一些比较常用的属性:

  • event.target:指的是触发事件的那个节点,也就是事件最初发生的节点。
  • event.target.matches:可以对关键节点进行匹配,来执行相应操作。
  • event.currentTarget:指的是正在执行的监听函数的那个节点。
  • event.isTrusted:表示事件是否是真实用户触发。
  • event.preventDefault():取消事件的默认行为。
  • event.stopPropagation():阻止事件的派发(包括了捕获和冒泡)。
  • event.stopImmediatePropagation():阻止同一个事件的其他监听函数被调用。

总结

事件机制在浏览器中非常有用,所有用户的交互型操作,都依赖于它。现代 JavaScript 框架应用中,我们也都离不开与原生事件的交互。

所以,在理解了事件流的概念,清楚了事件捕获与冒泡的顺序,掌握了一些原生事件的技巧之后,相信下次再遇到坑的时候,可以少走一些弯路了。

PS:欢迎关注我的公众号 “超哥前端小栈”,交流更多的想法与技术。

相关文章:

安装Ecshop首页出现报错:Only variables should be passed by referen

出现下面这就话&#xff1a; Strict Standards: Only variables should be passed by reference in D:\wamp\ecshop\includes\cls_template.php on line 406 第406行&#xff1a;$tag_sel array_shift(explode( , $tag)); 解决办法 1 5.3以上版本的问题&#xff0c;应该也和配…

KDD 2019高维稀疏数据上的深度学习Workshop论文汇总

作者 | 深度传送门来源 | 深度传送门【导读】本文是“深度推荐系统”专栏的第九篇文章&#xff0c;这个系列将介绍在深度学习的强力驱动下&#xff0c;给推荐系统工业界所带来的最前沿的变化。本文简要总结一下阿里妈妈在 KDD 2019 上组织的第一届面向高维稀疏数据的深度学习实…

C++中fstream的使用

C中处理文件类似于处理标准输入和标准输出。类ifstream、ofstream和fstream分别从类 istream、ostream和iostream派生而来。作为派生的类&#xff0c;它们继承了插入和提取运算符&#xff08;以及其他成员函数&#xff09;&#xff0c;还有与文件一起使用的成员和构造函数。可将…

浅谈Disruptor

Disruptor是一个低延迟(low-latency)&#xff0c;高吞吐量(high-throughput)的事件发布订阅框架。通过Disruptor&#xff0c;可以在一个JVM中发布事件&#xff0c;和订阅事件。相对于Java中的阻塞队列(ArrayBlockingQueue,LinkedBlockingQueue)&#xff0c;Disruptor的优点是性…

web 服务发布注意事项

1、在发布的时候首先查看服务器对外开放的端口&#xff0c;如果没有最好和客户进行沟通需要开放那些对应的端口&#xff0c;要不外界无法访问发布的站点。 2、在oracle需要远程控制服务器的数据库的时候需要开发1521端口。转载于:https://www.cnblogs.com/jzm53550629/p/337563…

OpenCV代码提取:resize函数的实现

之前在http://blog.csdn.net/fengbingchun/article/details/17335477 中有过对cv::resize函数五种插值算法的介绍。这里将OpenCV3.1中五种插值算法的代码进行了提取调整。支持N通道uchar和float类型。经测试&#xff0c;与OpenCV3.1结果完全一致。实现代码resize.hpp&#xff1…

IBM重磅开源Power芯片指令集?国产芯迎来新机遇?

整理 | 郭芮出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;自去年 IBM 以 340 亿美元收购了 Linux 巨头红帽之后&#xff0c;这家 107 岁的蓝色巨人终于又在开源方面有大动作了&#xff01;近日在 Linux 基金会开源峰会上&#xff0c;IBM 宣布向开源社区提供 Powe…

构造函数不能为虚/重载函数总结

构造函数不能为虚/重载函数总结 作为一个类&#xff0c;他最基础的成员函数就要数构造函数了。这里我们先探讨一下构造函数为什么不能是虚函数。 在解决这个问题之前&#xff0c;要先明白类中函数的调用方式。一个类的函数共用一个函数空间&#xff0c;因此在实例化的对象中是不…

通过data:image/png;base64把图片直接写在src里

2019独角兽企业重金招聘Python工程师标准>>> 关于用base64存储图片 网页上有些图片的src或css背景图片的url后面跟了一大串字符&#xff0c;比如&#xff1a;data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAYAAABIdFAMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZS…

算力“竞速”,企业AI落地的当务之急

充足的算力资源&#xff0c;在数据量持续增长及算法持续复杂化的前提下&#xff0c;无疑是保障人工智能应用落地效果的关键。软件定义算力——打造AI转型最佳实践8月2日&#xff0c;第四范式联合英特尔共同举办了AI实践者之声夏令营活动。第四范式基础架构负责人刘一鸣以《软件…

内存检测工具Dr. Memory的使用

Dr. Memory是一个内存调试工具&#xff0c;它是一个开源免费的内存检测工具&#xff0c;它能够及时发现内存相关的编程错误&#xff0c;比如未初始化访问、内存非法访问、数组越界读/写、以及内存泄露等。它可以在Linux、Windows、Mac OS和Android操作系统上使用。关于Dr. Memo…

手把手教你如何新建scrapy爬虫框架的第一个项目(下)

前几天小编带大家学会了如何在Scrapy框架下创建属于自己的第一个爬虫项目&#xff08;上&#xff09;&#xff0c;今天我们进一步深入的了解Scrapy爬虫项目创建&#xff0c;这里以伯乐在线网站的所有文章页为例进行说明。在我们创建好Scrapy爬虫项目之后&#xff0c;会得到上图…

.net完整的图文验证

摘自:http://blog.csdn.net/durongjian/article/details/4336380 一、创建ValidaeCode类库工程&#xff1a; 1、创建ValidaeCode类库工程&#xff0c;在[解决胜方案资源管理器]面板中&#xff0c;右键单击[ValidateCode]节点&#xff0c;并选择[属性]命令。 2、单击[属性]命令&…

Tesseract-OCR 3.04在Windows7 vs2013上编译过程

从https://github.com/tesseract-ocr/tesseract下载最新源码,commit id: 86acff5, 2016.06.07. 里面有个vs2010目录&#xff0c;用vs2013打开tesseract.sln。Tesseract依赖图像库Leptonica&#xff0c;Leptonica的编译过程可以参考http://blog.csdn.net/fengbingchun/article/d…

【Laravel-海贼王系列】第九章, Events 功能解析

Events 注册 框架如何在启动的时候加载注册的事件?框架如何触发事件?1&#xff0c;先在容器中注册 events 的全局对象。 Application 构造函数中对 events 进行注册代码 protected function registerBaseServiceProviders(){$this->register(new EventServiceProvider($th…

触类旁通,经典面试题最长公共子序列应该这么答

作者 | labuladong来源 | labuladong&#xff08;ID:labuladong)【导读】最长公共子序列&#xff08;Longest Common Subsequence&#xff0c;简称 LCS&#xff09;是一道非常经典的面试题目&#xff0c;因为它的解法是典型的二维动态规划&#xff0c;大部分比较困难的字符串问…

两分公支的IPSec***流量走总部测试

一.概述&#xff1a;在论坛上看到一个朋友发帖希望两个分支的IPSEC ***流量经过总部&#xff0c;如是搭建拓扑测试了一下&#xff0c;因为跑两个VM版的ASA8.42机器性能不过&#xff0c;所以用PIX8.0来代替ASA,应该主要配置都跟ASA8.0差不多。二.基本思路&#xff1a;A.两个分支…

OpenCV代码提取:cvtColor函数的实现

OpenCV中的cvtColor函数包括了很多颜色格式之间的转换&#xff0c;用起来很方便&#xff0c;这里对cvtColor函数的code进行了提取&#xff0c;经测试&#xff0c;和OpenCV3.1结果完全一致。实现代码cvtColor.hpp:// fbc_cv is free software and uses the same licence as Open…

关于java.util.LinkedHashMap cannot be cast to ......的解决办法

今天在项目中遇到一个问题&#xff0c;接口接收到list在对list进行遍历的时候报出如下错误: 断点看一下这个list感觉没有任何的问题: 那为什么会报这个错误呢 这个接口是这样的&#xff0c;在想会不会是json在转list的时候把这个list给整坏了。 于是&#xff0c;我把这个list再…

三两下实现NLP训练和预测,这四个框架你要知道

作者 | 狄东林 刘元兴 朱庆福 胡景雯编辑 | 刘元兴&#xff0c;崔一鸣来源 | 哈工大SCIR&#xff08;ID:HIT_SCIR)引言随着人工智能的发展&#xff0c;越来越多深度学习框架如雨后春笋般涌现&#xff0c;例如PyTorch、TensorFlow、Keras、MXNet、Theano 和 PaddlePaddle 等。这…

大学计算机基础实验

下载2013算法实验报告.rar转载于:https://www.cnblogs.com/shajianheng/p/3381968.html

java基础(十三)-----详解内部类——Java高级开发必须懂的

java基础(十三)-----详解内部类——Java高级开发必须懂的 目录 为什么要使用内部类内部类基础静态内部类 成员内部类 成员内部类的对象创建继承成员内部类局部内部类推荐博客匿名内部类正文 可以将一个类的定义放在另一个类的定义内部&#xff0c;这就是内部类。 回到顶部为什么…

C++中函数指针的使用

A function pointer is a variable that stores the address of a function that can later be called through that function pointer. This is useful because functions encapsulate behavior.函数指针是一个指向函数的指针,函数指针表示一个函数的入口地址。指针是变量&…

只做好CTR预估远不够,淘宝融合CTR、GMV、收入等多目标有绝招

作者 | 吴海波转载自知乎用户吴海波【导读】一直以来&#xff0c;电商场景就存在 ctr、cvr、gmv、成交 uv 等多个目标&#xff0c;都是核心指标。理想情况下&#xff0c;提升 ctr 就能提升 gmv&#xff0c;但本文作者认为&#xff0c;在一定程度上&#xff0c; ctr 和 gmv 并不…

Android监听HOME按键

2019独角兽企业重金招聘Python工程师标准>>> <!-- lang: java --> class HomeKeyEventBroadCastReceiver extends BroadcastReceiver {static final String SYSTEM_REASON "reason";static final String SYSTEM_HOME_KEY "homekey";// …

OpenCV代码提取:merge/split函数的实现

对OpenCV中的merge/split函数进行了实现&#xff0c;经测试&#xff0c;与OpenCV3.1结果完全一致。merge实现代码merge.hpp&#xff1a;// fbc_cv is free software and uses the same licence as OpenCV // Email: fengbingchun163.com#ifndef FBC_CV_MERGE_HPP_ #define FBC_…

DeepMind提图像生成的递归神经网络DRAW,158行Python代码复现

作者 | Samuel Noriega译者 | Freesia编辑 | 夕颜出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】最近&#xff0c;谷歌 DeepMInd 发表论文( DRAW: A Recurrent Neural Network For Image Generation&#xff09;&#xff0c;提出了一个用于图像生成的递归神…

其他进制的数字

JS中如果需要表示16进制的数字,则需要以0X开头 0X10 八进制数字以0开头 070 070有些浏览器会以8进制解析,但是有些则用10进制解析,10进制为70,8进制为56 所以parseint() 第二个参数可以设定进制,比如 parseint(“070”,10)代表以10进制解析070 2进制以0b开头,但是不是所有浏览…

java中的移位运算符

移位运算符是在数字的二进制形式上进行平移。主要有左移&#xff08;<<&#xff09;、带符号右移&#xff08;>>&#xff09;以及无符号右移&#xff08;>>>&#xff09;。左移运算符&#xff08;<<&#xff09;的运算规则为&#xff1a;按二进制形…

C++11中nullptr的使用

在C语言中&#xff0c;NULL实际上是一个void* 的指针&#xff0c;然后把void* 指针赋值给其它类型的指针的时候&#xff0c;会隐式转换成相应的类型。而如果用一个C编译器来编译的时候是要出错的&#xff0c;因为C是强类型的&#xff0c;void* 是不能隐式转换成其它指针类型的。…