【学习笔记】js 运算符

运算符

1.new运算符

  • new constructor[([arguments])]

  • 创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

  • 会进行如下的操作:
    ①.创建一个空的简单JavaScript对象(即{});
    ②.链接该对象(即设置该对象的构造函数)到另一个对象 ;
    ③.将步骤1新创建的对象作为this的上下文 ;
    ④.如果该函数没有返回对象,则返回this。

  • 当代码 new Foo(…) 执行时,会发生以下事情:

①.一个继承自 Foo.prototype 的新对象被创建。

②.使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。

③.由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

如果你没有使用 new 运算符, 构造函数会像其他的常规函数一样被调用, 并不会创建一个对象。在这种情况下, this 的指向也是不一样的。

对象属性为其他对象

function Person(name,age,sex) {
  this.name = name;
  this.age = age;
  this.sex =sex;
}
var lala = new Person("lala",12,"men")


function Candy(type,color,size,owner) {
  this.type = type;
  this.color = color;
  this.size = size;
  this.owner = owner;
}
此时candy1的owner属性值为lala对象
var candy1 = new Candy("lolpop","red",12,lala);
console.log(candy1.owner.age);

2.void运算符

  • void expression
  • 在它右边的操作数会正常计算,但是无论结果是什么,void都会返回undefined。
  • 一元运算符,出现在操作数的左边,操作数可以是任意类型的值,void右边的表达式可以是带括号形式(例如:void(0)),也可以是不带括号的形式(例如:void 0)。

(1)立即调用的函数表达式

  • 在使用立即执行的函数表达式时,可以利用 void 运算符让 JavaScript 引擎把一个function关键字识别成函数表达式而不是函数声明(语句)。
void function getcandy () {
  var candycolor = function () {};
  candycolor();
}();

(2)void 0

  • 使用void(0)是用来代替undefined,因为undefind不是JS的保留字。在某些浏览器下undefind的值可能会被修改。
  • 下面所有的结果都是undefined:
console.log(void 0); 

console.log(void(0)); 

console.log(void ‘hello word!); 

console.log(void(true));
  • 当点击某个文字链接时产生某个动画效果但保持页面不刷新时,可以使用下面这种形式:
    <a href=“javascript:void(0);” / >

3.条件运算符

  • condition ? expr1 : expr2

  • 条件(三元)运算符是 JavaScript 仅有的使用三个操作数的运算符。本运算符经常作为if语句的简短形式来使用。

  • 如果condition为true,运算符就会返回 expr1 的值;否则, 就会返回 expr2 的值。

var age = 26;
var canDrinkAlcohol = (age > 21) ? "True, over 21" : "False, under 21";
console.log(canDrinkAlcohol); // "True, over 21"

4. 赋值运算符

  • 基本的赋值运算符是等号(=),该运算符把它右边的运算值赋给左边。即,x = y 把 y 的值赋给 x。

(1)简写形式

在这里插入图片描述

(2)不同数据类型的加赋值

// 定义下列变量
//  foo = 'foo'
//  bar = 5
//  baz = true

和数字相加 结果都是数字(字符串除外)
// Number + Number -> addition
bar += 2 // 7

// Boolean + Number -> addition
baz += 1 // 2
布尔和布尔相加为1
// Boolean + Boolean -> addition
baz += false // 1
和字符串相加 结果都是字符串
// Number + String -> concatenation
bar += 'foo' // "5foo"

// String + Boolean -> concatenation
foo += false // "foofalse"

// String + String -> concatenation
foo += 'bar' // "foobar"

(3)除加赋值以外的其他类型赋值

不同数据类型

-NaN

// 假定已定义了下面的变量
//  bar = 5
bar -= "foo" // NaN
bar *= 'foo' // NaN
bar /= "foo" // NaN
bar %= 'foo' // NaN
bar **= 'foo' // NaN
相同数据类型

bar /= 0 // Infinity
bar %= 0 // NaN

5.算数运算符

  • 算术运算符以数值(字面量或变量)作为其操作数,并返回一个单个数值。标准算术运算符是加法(+),减法( - ),乘法(*)和除法(/)。
  • 一元负号 (-) 一元正号 (+)
  • 求余 (%)
    取模功能就是 var1 除以 var2 的整型余数。
12 % 5 // 2
-1 % 2 // -1
NaN % 2 // NaN
1 % 2 // 1
2 % 3 // 2
-4 % 2 // -0
5.5 % 2 // 1.5

  • 幂运算符是右结合的。a ** b ** c 等同于 a ** (b ** c)。
2 ** 3 // 8
3 ** 2 // 9
3 ** 2.5 // 15.588457268119896
10 ** -1 // 0.1
NaN ** 2 // NaN

2 ** 3 ** 2 // 512
2 ** (3 ** 2) // 512
(2 ** 3) ** 2 // 64
  • 递增 (++)
    为其操作数增加1,返回一个数值。
    后置 将会在递增前返回数值。
    前置 将会在递增后返回数值。
// 后置 
var x = 3;
y = x++; 
// y = 3, x = 4

// 前置
var a = 2;
b = ++a; 
// a = 3, b = 3
  • 递减 (–)
    将其操作数减去1,并返回一个数值。
    前置 在递减后返回数值。
    后置 在递减前返回数值。
// 后置 
var x = 3;
y = x--; // y = 3, x = 2

// 前置
var a = 2;
b = --a; // a = 1, b = 1

6.逻辑运算符

  • 逻辑运算符通常用于布尔型(逻辑)值。这种情况下,它们返回一个布尔值。
  • 然而,&& 和 || 运算符会返回一个指定操作数的值,因此,这些运算符也用于非布尔值。这时,它们也就会返回一个非布尔型值。
    expr可能是任何一种类型, 不一定是布尔值
    在这里插入图片描述
    如果一个值可以被转换为 true,那么这个值就是所谓的 truthy,如果可以被转换为 false,那么这个值就是所谓的 falsy。
  • 会被转换为 false 的表达式有:

null;
NaN;
0;
空字符串("" or ‘’ or ``);
undefined。

尽管 && 和 || 运算符能够使用非布尔值的操作数, 但它们依然可以被看作是布尔操作符,因为它们的返回值总是能够被转换为布尔值。

如果要显式地将它们的返回值(或者表达式)转换为布尔值,请使用双重非运算符(即!!)或者Boolean构造函数。

(1)逻辑与(&&)

a1 = true  && true      // t && t 返回 true
a2 = true  && false     // t && f 返回 false
a3 = false && true      // f && t 返回 false
a4 = false && (3 == 4)  // f && f 返回 false
a5 = "Cat" && "Dog"     // t && t 返回 "Dog"
a6 = false && "Cat"     // f && t 返回 false
a7 = "Cat" && false     // t && f 返回 false
a8 = ''    && false     // f && f 返回 ""
a9 = false && ''        // f && f 返回 false

(2)逻辑或(||)

o1 = true  || true      // t || t 返回 true
o2 = false || true      // f || t 返回 true
o3 = true  || false     // t || f 返回 true
o4 = false || (3 == 4)  // f || f 返回 false
o5 = "Cat" || "Dog"     // t || t 返回 "Cat"
o6 = false || "Cat"     // f || t 返回 "Cat"
o7 = "Cat" || false     // t || f 返回 "Cat"
o8 = ''    || false     // f || f 返回 false
o9 = false || ''        // f || f 返回 ""

(3)逻辑非(!)

n1 = !true              // !t 返回 false
n2 = !false             // !f 返回 true
n3 = !''                // !f 返回 true
n4 = !'Cat'             // !t 返回 false

(4)双重非(!!)

可能使用双重非运算符的一个场景,是显式地将任意值强制转换为其对应的布尔值。这种转换是基于被转换值的 “truthyness” 和 "falsyness"的(参见 truthy 和 falsy)。

同样的转换可以通过 Boolean 函数完成。

n1 = !!true                   // !!truthy 返回 true
n2 = !!{}                     // !!truthy 返回 true: 任何 对象都是 truthy 的…
n3 = !!(new Boolean(false))   // …甚至 .valueOf() 返回 false 的布尔值对象也是!
n4 = !!false                  // !!falsy 返回 false
n5 = !!""                     // !!falsy 返回 false
n6 = !!Boolean(false)         // !!falsy 返回 false

7.圆括号运算符

( )
用于控制表达式中的运算优先级。
圆括号运算符由一对圆括号组成,包裹表达式和子表达式用来覆盖常规的运算符优先级,达到低优先级的表达式比高优先级的表达式更早运算。

8.typeof运算符

typeof operand
typeof(operand)

  • 返回一个字符串,表示未经计算的操作数的类型。
  • 在这里插入图片描述
***数值***
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof(42) === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === 'number'; // Number 会尝试把参数解析成数值

typeof 42n === 'bigint';


***字符串***
typeof '' === 'string';
typeof 'bla' === 'string';
typeof `template literal` === 'string';
typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串
typeof (typeof 1) === 'string'; // typeof 总是返回一个字符串
typeof String(1) === 'string'; // String 将任意值转换为字符串,比 toString 更安全


***布尔值***
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(1) === 'boolean'; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!(1) === 'boolean'; // 两次调用 ! (逻辑非) 操作符相当于 Boolean()


***Symbols***
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';


***Undefined***
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined'; 


***对象***
typeof {a: 1} === 'object';

 使用 Array.isArray 或者 Object.prototype.toString.call
 区分数组和普通对象
typeof [1, 2, 4] === 'object';

typeof new Date() === 'object';
typeof /regex/ === 'object';


// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';

***函数***
typeof function() {} === 'function';
typeof class C {} === 'function'
typeof Math.sin === 'function';

***JavaScript 诞生以来便如此***
typeof null === 'object';

在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 “object”。

9.扩展运算符

  • 函数调用/数组构造时, 将数组表达式或者string在语法层面展开;
  • 在构造字面量对象时, 将对象表达式按key-value的方式展开。
  • 字面量一般指 [1, 2, 3] 或者 {name: “mdn”} 这种简洁的构造方式)
  • 在数组或函数参数中使用展开语法时, 该语法只能用于 可迭代对象

1.函数调用:

myFunction(…iterableObj);

  • 等价于apply的方式
  • 如果想将数组元素迭代为函数参数,一般使用Function.prototype.apply 的方式进行调用
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction(...args);

所有参数都可以通过展开语法来传值,也不限制多次使用展开语法。

function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);

在 new 表达式中应用

var dateFields = [1970, 0, 1]; // 1970年1月1日
var d = new Date(...dateFields);

2.构造字面量数组调用

  • 和参数列表的展开类似, … 在构造字面量数组时, 可以在任意位置多次使用.
var parts = ['shoulders', 'knees']; 
var lyrics = ['head', ...parts, 'and', 'toes']; 
// ["head", "shoulders", "knees", "and", "toes"]
(1)数组的连接
  • 等价于Array.concat方法 用于将一个数组连接到另一个数组的后面
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2];
  • 等价于Array.unshift 方法 用于在数组的开头插入新元素/数组.
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1]; // arr1 现在为 [3, 4, 5, 0, 1, 2]

请注意, 这里使用展开语法创建了一个新的 arr1 数组, Array.unshift 方法则是修改了原本存在的 arr1 数组

(2)数组的拷贝
  • 和Object.assign() 行为一致, 执行的都是浅拷贝(只遍历一层)
  • 不能对多维数组进行深拷贝
var arr = [1, 2, 3];
var arr2 = [...arr]; // like arr.slice()
arr2.push(4); 

 arr2 此时变成 [1, 2, 3, 4]
 arr 不受影响

3.构造字面量对象调用

  • 将已有对象的所有可枚举(enumerable)属性拷贝到新构造的对象中.
  • 浅拷贝(Shallow-cloning, 不包含 prototype) 和对象合并, 可以使用扩展运算符。而不必再使用 Object.assign() 方式.
  • Object.assign() 函数会触发 setters,而扩展运算符则不会。
  • 不能替换或者模拟 Object.assign() 函数
(1)对象的克隆
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };

var clonedObj = { ...obj1 };
// 克隆后的对象: { foo: "bar", x: 42 }
(2)对象的合并
var mergedObj = { ...obj1, ...obj2 };
// 合并后的对象: { foo: "baz", x: 42, y: 13 }
//等同于
var mergedObj = Object.assign({},obj1,obj2);
  • 如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。
let aWithOverrides = { ...a, x: 1, y: 2 };
// 等同于
let aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
// 等同于
let x = 1, y = 2, aWithOverrides = { ...a, x, y };
// 等同于
let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 });

上面代码中,a对象的x属性和y属性,拷贝到新对象后会被覆盖掉。
这个特性可以用来修改现有对象部分的属性

let newVersion = {
  ...previousVersion,
  name: 'New Name' // Override the name property
};

上面代码中,newVersion对象自定义了name属性,其他属性全部复制自previousVersion对象。

  • 如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值。
let aWithDefaults = { x: 1, y: 2, ...a };
// 等同于
let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
// 等同于
let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);
(4)对象里面是数组的情况
let foo = { ...['a', 'b', 'c'] };
foo
// {0: "a", 1: "b", 2: "c"}
(5)对象的解构赋值
  • 将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。
  • 所有的键和它们的值,都会拷贝到新对象上面。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
  • 要求等号右边也是对象,如果右边是undefined或null就会报错
  • 必须是最后一个参数,否则报错
  • 解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。
let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2

上面代码中,x是解构赋值所在的对象,拷贝了对象obj的a属性。a属性引用了一个对象,修改这个对象的值,会影响到解构赋值对它的引用。

  • 扩展运算符的解构赋值,不能复制继承自原型对象的属性。
let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3 // { b: 2 }
o3.a // undefined

上面代码中,对象o3复制了o2,但是只复制了o2自身的属性,没有复制它的原型对象o1的属性。

4.不可迭代对象不能调用

var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable

10.相等运算符

(1)相等(== )和严格相等(===)

是否类型转换
  • 相等(==)会为两个不同类型的操作数转换类型,然后进行严格比较。
1   ==  1     // true
"1"  ==  1     // true
 1   == '1'    // true
 0   == false  // true
  • 严格相等 (===) 不会进行类型转换,仅当操作数严格相等时返回true
 3 === 3   // true
3 === '3' // false
var object1 = {"value":"key"}, object2={"value":"key"};
object1 === object2 //false

总结:
== 只比较值
=== 不仅比较值,还比较数据类型

原始值和引用值
//原始值
var a = 1;
var b = true;
console.log(a == b); //ture
console.log(a === b);//false

//引用值
var obj1 = {};
var obj2 = {};
console.log(obj1 == obj2);//false
console.log(obj1 === obj2);//false

引用值 obj1 和 obj2 分别引用的是存放在堆内存中的两个不同的对象,所以 obj1 和 obj2 的值(地址引用)不一样。

总结:
原始值的比较是 值 的比较
引用值的比较是 引用(地址) 的比较

(2)不相等(!==)和 严格不相等 (! == )

性质同上

11.关系运算符

都是左边比较右边:
大于>
大于等于 >=
小于 <
小于等于 <=

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值