中高级前端? 这些一元运算符,你真的搞清楚了吗

news2024/11/20 0:35:27

前言

一元运算符,不太起眼,作用很大,请别忽视她! 走近她,爱上她!

定义

只需要一个操作数的运算符称为一元运算符。

还是代码容易懂:

+1 // 一个操作数

1 + 2 // 两个操作数

一元运算符清单

运算符说明
delete删除对象的属性
void对给定的表达式进行求值,然后返回 undefined
typeof返回数据的基本类型
+将操作转换为Number类型
-将操作转换为Number类型并取反
~按位非运算符
!逻辑非运算符

delete

基本说明

delete 操作符用于删除对象的某个属性。
对于所有情况都是true, 除非属性是一个自身的 不可配置的属性。
在这种情况下,

  • 非严格模式返回 false。
  • 在严格模式下,如果是属性是一个自己不可配置的属性,会抛出TypeError。

什么是不可配置属性, 看一段代码就知道:

var Employee = {};
Object.defineProperty(Employee, 'name', { 
    configurable: false
});

console.log(delete Employee.name);  // returns false

通过Object.defineProperty设置 configurable: false

注意事项

除了很多属性删除不掉外,还有下面要注意:

  1. delete 删除不了原型上的属性

  2. 当你删除一个数组元素时,数组的长度不受影响。

  3. delete 方法会可能破坏对象的形状,同样会导致 V8 为该对象重新生成新的隐藏类,导致性能降低。

    关于delete的性能问题更多可参见 Why系列:如无必要, don’t 使用delete

  4. 在严格模式下,如果对一个变量的直接引用、函数的参数或者函数名使用delete操作,将会抛出语法错误(SyntaxError)

function func(param) {
  // SyntaxError in strict mode.
  console.log(delete param); // false
}

实战

我们平时可能碰不到使用delete的场景。
我们临时在对象上添加一些临时方法或者中间状态值,进行一些列操作后,再删除这些属性。

来一个经典案列,手写call:

// 不考虑node环境, 不考虑严格模式, 不考虑被freeze
Function.prototype.call = function (context){
    if (typeof this !== 'function') {
        throw new TypeError('not a funciton')
    }    
    var argsArr = [];
    for(var i = 1; i< arguments.length; i++){
        argsArr.push("arguments[" + i + "]");
    }    
    var  context  = context == null ? window : Object(context);   
    // 保留现场
    var hasFn =  ("__fn__" in context); // 不推荐做法
    var originFn;
    if(hasFn){
       originFn = context.__fn__;
    }; 
    
    context.__fn__ =  this;
    
    var code = "context.__fn__(" +  argsArr.join(",")  +  ")";
    var result = eval(code);

    if(hasFn){
        context.__fn__ = originFn
    }else {
        delete context.__fn__;
    }
    return result;    
}

我们把context的属性__fn__来指向当前函数,执行完毕之后,根据之前是否也有__fn__属性,来决定是delete还是复原原值。

void

基本说明

void 运算符 对给定的表达式进行求值,然后返回 undefined。

使用场景

老版本的的一些浏览器 undefined 是可以被改写的,看图:
这是我用IE11仿真IE8的效果

image.png

所以,所以在这种情况下 void 0undefined靠谱。
void 这个一元运算法除了这个 准备返回 undefined外, 还有另外两件常见的用途:

  1. a标签的href,就是什么都不做
    <a href="javascript:void(0);">

  2. IIFE立即执行

;void function(msg){
    console.log(msg)
}("你好啊");

当然更直接的方式是:

;(function(msg){
    console.log(msg)
})("你好啊");

因箭头函数的诞生,有一个新的用途:在箭头函数中避免泄漏

button.onclick = () => void doSomething();

typeof

操作符返回一个字符串,表示未经计算的操作数的类型。

ES5以前的基础数据类型大家倒背如流,typeof null === "object" 这个梗自然也不用提。
Symbol (ECMAScript 2015 新增) 也是常客,BigInt 也应该不陌生。

typeof BigInt(10) // bigint
typeof Symbol.for("key") // symbol

暂时性死区

加入了块级作用域的 letconst 之后,在其被声明之前对块中的 let 和 const 变量使用 typeof 会抛出一个 ReferenceError。 我们称之为 暂时性死区

typeof newLetVariable; // ReferenceError
typeof newConstVariable; // ReferenceError
typeof newClass; // ReferenceError

let newLetVariable;
const newConstVariable = 'hello';
class newClass{};

特别的document.all

typeof document.all === 'undefined'; // true

document.all返回页面的所有元素,应该说不算是一个标准的API.
IE4时代的产物,这个甚至作为判断是不是IE浏览器的方法。

if(document.all){
    //is IE
}

其他浏览器厂商觉得这个document.all很有用,但是又不想影响现有运行的网站。 所以, document.all 返回的是所有元素,但是typeof的值却是undefined,是不是有点好笑。

甚至到IE10都没改变:

image.png

IE11终结了这种状态:

image.png

判断数据类型的方法

  1. typeof
    识别基础数据类型和引用数据类型。
  2. instanceof
    识别引用类型,基本原理查询原型链。
  3. constructor
    识别引用类型,但是这个玩意不准,contructor可以被改写, 次选方案吧。
  4. Object.prototype.toString
    识别基础数据类型和引用数据类型。
  5. 鸭子类型检测
    我们看看周下载量达到800万 is-promise库的源码
function isPromise(obj) {
  return !!obj && (typeof obj === 'object' || typeof obj === 'function') 
  && typeof obj.then === 'function';
}

当然结合使用效果更佳。

+-

先来一个趣味题: 求最后的输出结果

var a = b = 1;
console.log(+a++ + + +b++)  // 2  等同于 +a + + +b  == a + b

var a = b = 1;
console.log(++a+ + + ++b)  // 4   等同于 (++a)+ + + (++b) ==  a + b

var a = b = 1;
console.log(-a-- + - -b--) // 0   等同于 -a + - -b = -a + b

一元 + 运算符将其操作数转换为数字类型。

参数类型结果
UndefinedNaN
Null+0
Booleantrue返回1,反之返回0
String空字符串变为0,如果出现任何一个非有效数字字符,结果都是NaN
Number不转换,返回原值
SymbolTypeError
BigIntTypeError
ObjecttoPrimitive=>valueOf=>toString=>Number()
  1. 先调用对象的 Symbol.toPrimitive 这个方法,如果不存在这个方法
  2. 再调用对象的 valueOf 获取原始值,如果获取的值不是原始值
  3. 再调用对象的 toString 把其变为字符串
  4. 最后再把字符串基于Number()方法转换为数字

更多转换细节 Unary + Operator

一元-+ 极其类似,只不过最后再取个反。

~

将其操作数的位反转, 这可能不太好理解。
给个公式你就能明白了

~x = -(x + 1)

其还会去掉小数位, 是去掉,不是四舍五入

~2.3  // -3
~2.6  // -3

来个例子~5 = -(5+1) = -6

console.log(~5);// -6

取反

那么我们就可以根据这写一个取反函数 :

function reveNumber (val: number) {
  return ~val + 1
}

推导:

~val + 1 = -(val + 1) + 1 = -val - 1 + 1  = -val

取整数位

这个我想肯定有很多人用过

function getIntPart(val: number){
    return ~~val;
}

toInt(3.4)  // 3

推导:
因为~会丢掉小数部分,来回折腾两次就只剩下整数部分啦。

 ~~val = ~(-(val + 1))  = -(-(val + 1) + 1) = -(-val -1 + 1) = val

额外注意:
由于对数字~-1~4294967295 (232-1) 使用32位表示形式,结果均为0。

!

取反,返回布尔值。

下面情况返回真,其余为假:

  • null
  • NaN
  • 0
  • "" or '' or ``
  • undefined

双非(!!)

两个运算符显式地强制将任何值转换为相应的布尔值。

!!({}) // true

我们经常喜欢用 !!!进行是非判断,其实是不严密,这点一定要清除,所以最好加上一些前置判断或者明确的知道数据类型。

混淆之数字

我们可以通过一些符号生成数字和字母,在混淆的时候有用,比如:

+[]  // 0
+!+[] // 1 
+!+[]+!+[] // 2
(![]+[])[+!![]] // a

是不是很有意思, 主要思路就是类型转换+数组下标

写在最后

如果你觉得不错,你的一赞一评就是我前行的最大动力。

技术交流群请到 这里来。
或者添加我的微信 dirge-cloud,一起学习。

参考引用

一元运算符
重学js —— 一元运算符(delete/void/typeof/+/-/~/!)
深入理解 JavaScript 中的 delete 操作符
深入分析 delete
~,&,|,^等位运算符在JavaScript中的一些应用
记一次校招面试回忆,迟来的位运算符小解
implicit.js
t 中的 delete 操作符](https://www.w3cplus.com/javascript/deep-in-delete.html)
深入分析 delete
~,&,|,^等位运算符在JavaScript中的一些应用
记一次校招面试回忆,迟来的位运算符小解
implicit.js

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1570439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MacOS - brew 和 brew cask 有什么区别?

brew 是 ruby 的包管理&#xff0c;后来看 yangzhiping 的博客介绍了 brew cask&#xff0c;感觉 cask 是更好的关联关系管理&#xff0c;但是&#xff0c;我后来使用过程中&#xff0c;发现很多软件 brew cask 里没有&#xff0c;但是 brew 里面倒是挺多&#xff01;今天来给说…

一键批量高效记账,支持通过关键词来筛选某个人的借款记录,方便高效管理收支明细

随着生活节奏的加快&#xff0c;个人和企业财务管理变得越来越复杂。尤其是在处理大量记账任务时&#xff0c;如何快速、准确地完成&#xff0c;并且能够方便地追踪和筛选特定借款记录&#xff0c;成为了许多人关注的焦点。现在&#xff0c;我们为您提供一款全新的财务管理工具…

小程序appsecret在哪里看

问题开发中需要用到appid和secret。但找不到secret。后面发现是在微信开发者平台自己生成才行 微信公众平台 (qq.com) 在开发者平台的开发管理当中&#xff0c;生产即可

U盘位置不可用?数据恢复有高招!

在我们日常生活和工作中&#xff0c;U盘已经成为不可或缺的数据存储工具。然而&#xff0c;有时我们会遭遇一个令人头疼的问题&#xff1a;将U盘插入电脑后&#xff0c;系统提示“U盘位置不可用”。这究竟意味着什么呢&#xff1f;简单来说&#xff0c;这就是电脑无法识别或访问…

mysql慢sql排查与分析

当MySQL遇到慢查询&#xff08;慢SQL&#xff09;时&#xff0c;我们可以通过以下步骤进行排查和优化&#xff1a; 标题开启慢查询日志&#xff1a; 确保MySQL的慢查询日志已经开启。通过查看slow_query_log和slow_query_log_file变量来确认。 如果没有开启&#xff0c;可以…

新项目视频号小店挣钱吗?可全职可副业可量化,新蓝海项目来了!

大家好&#xff0c;我是电商花花。 一个有潜力的新项目&#xff0c;自然会吸引到很多创业者的追捧&#xff0c;尤其是&#xff0c;目前最火的视频号小店无货源项目&#xff0c;可以说是继抖音小店无货源电商之外又一匹黑马。 今年想创业的人不妨看一看&#xff0c;视频号小店值…

chatGPT4无法登录

遇到问题&#xff1a;chatgpt网站上点击登录&#xff08;log in),网站就会跳转并显示&#xff1a;unable to connect 解决方法&#xff1a;不要用亚洲节点&#xff0c;亚洲节点被全面封禁&#xff0c;在全局代理中可以换成美国的节点

约数与倍数-第12届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第45讲。 约数与倍数&#…

基于SpringBoot Vue学生信息管理

一、&#x1f4dd;功能介绍 基于SpringBoot Vue学生信息管理 角色&#xff1a;管理员、学生、教师 管理员&#xff1a;管理员进入主页面&#xff0c;主要功能包括对系统首页、个人中心、学生管理、教师管理、公告通知管理、课程类型管理、课程信息管理、选课信息管理、课程成…

Java 关键字 this 使用详解(通俗易懂)

this关键字主要有以下三个地方使用 在方法体中引用当前对象&#xff0c;即其方法被调用的对象&#xff0c;以便将当前对象的实例变量或当前对象作为参数传递给其他方法。 ① t this.x; 要在方法中引用当前对象&#xff0c;可以使用关键字 this。 ② return this; 作为当前…

Spring注解开发和XML开发

目录 Spring简介发展史Spring Framework系统架构spring 核心概念IOC、IOC容器、Bean、DIIOC快速入门DI快速入门 IOCBean基础配置id与class属性name属性scope属性 Bean的实例化构造方法静态工厂实例工厂FactoryBean的使用&#xff08;工厂实例的简化&#xff09; Bean的生命周期…

160 Linux C++ 通讯架构实战14,epoll 反应堆模型

到这里&#xff0c;我们需要整理一下之前学习的epoll模型&#xff0c;并根据之前的epoll模型&#xff0c;提出弊端&#xff0c;进而整理epoll反应堆模型&#xff0c;进一步深刻理解&#xff0c;这是因为epoll实在是太重要了。 复习之前的epoll的整体流程以及思路。 参考之前写…

DHCP工作过程以及抓包分析

从PC1的e0/0/1接口进行抓包 客户端基于UDP、源端口68、目标端口67进行广播请求&#xff0c;源IP0.0.0.0&#xff0c;&#xff08;无效地址&#xff0c;代表本地无地址&#xff09;目标IP255.255.255.255&#xff1b; 从下面截图可以看出&#xff1a; 源mac为电脑mac&#xff…

采用C#.net6.0+Vue,Ant-Design技术开发的一套大型医院手术麻醉信息系统源码,系统成熟,运行稳定

手术麻醉信息系统源码&#xff0c;C#手麻系统源码&#xff0c;自主版权应用案例&#xff08;适合上项目&#xff09; 手术麻醉信息系统可以实现手术室监护仪、麻醉机、呼吸机、输液泵等设备输出数据的自动采集&#xff0c;采集的数据能据如实准确地反映患者生命体征参数的变化&…

【Leetcode笔记】102.二叉树的层序遍历

目录 知识点Leetcode代码&#xff1a;ACM模式代码&#xff1a; 知识点 vector、queue容器的操作 对vector<int> vec;做插入元素操作&#xff1a;vec.push_back(x)。对queue<TreeNode*> que;做插入元素操作&#xff1a;que.push(root);。队列有四个常用的操作&…

Redis从入门到精通(六)Redis实战(三)优惠券秒杀

↑↑↑下载测试项目原代码↑↑↑ 文章目录 前言4.3 优惠券秒杀4.3.1 数据表与实体类4.3.2 添加优惠券4.3.2.1 添加普通券代码4.3.2.2 添加秒杀券代码 4.3.3 实现秒杀下单4.3.3.1 秒杀下单逻辑分析4.3.3.2 获取秒杀订单ID4.3.3.3 获取用户ID4.3.3.4 实现秒杀下单 前言 Redis实战…

团体程序设计天梯赛-练习集 01

天梯赛题解合集 团体程序设计天梯赛-练习集 (L1-001 - L1-012) 团体程序设计天梯赛-练习集 (L1-013 - L1-024) 团体程序设计天梯赛-练习集 (L1-025 - L1-036) 团体程序设计天梯赛-练习集 (L1-037 - L1-048) L1-001 Hello World 输出题 样例 输入 输出 Hello World!思…

笔记本电脑win7 Wireless-AC 7265连不上wifi6

1.背景介绍 旧路由器连接人数有限&#xff0c;老旧&#xff0c;信号不稳定更换了新路由器&#xff0c;如 TL-XDR5430易展版用户电脑连不上新的WIFI网络了&#xff0c;比较着急 核心问题&#xff1a;有效解决笔记本连接wifi上网问题&#xff0c;方法不限 2.环境信息 Windows…

深入探索MySQL:成本模型解析与查询性能优化,及未来深度学习与AI模型的应用展望

码到三十五 &#xff1a; 个人主页 在数据库管理系统中&#xff0c;查询优化器是一个至关重要的组件&#xff0c;它负责将用户提交的SQL查询转换为高效的执行计划。在MySQL中&#xff0c;查询优化器使用了一个称为“成本模型”的机制来评估不同执行计划的优劣&#xff0c;并选择…

No dashboards are active for the current data set.

再次记录一下这个离谱的问题 之前出现这个问题是因为目录没写对 今天遇到这个问题的原因是目录是对的&#xff0c;跟目录是否带有中文也没关系 是writer写入的时候写的是空的&#xff0c;离谱的是写入是空的情况下也会生成events日志文件&#xff0c;看起来好像成功写入了一样&…