# JavaScript基础知识点

# 数据类型

  1. 原始类型有null, undefined, string, number, boolean, symbolbigInt

所以null基本类型就不是object,虽然typeof null会输出object是因为历史遗留。在JS的最初版本中使用的是32位系统,为了性能考虑使用低位存储变量的类型信息,000开头代表是对象,然而null表示为全零,所以将它错误的判断为object

  1. 除了原始类型其他都是对象类型

原始类型存储的是值,对象类型存储的是地址(指针)。当创建一个对象类型的时候,计算机会在内存中帮我们开辟一个空间来存放值,但是我们需要找到这个空间,这个空间会拥有一个地址(指针)

const b = []
1

TIP

对于常量b来说,假设内存地址为#001,那么在地址#001的位置存放了值[],常量b存放了地址#001

  • undefined典型用法
    • 变量被声明了,但没有赋值时,就等于 undefined
    • 调用函数时,应该提供的参数没有提供,该参数等于undefined
    • 对象没有赋值的属性,该属性的值为 undefined
    • 函数没有返回值时,默认返回 undefined

undefinednull的区别: 在 JavaScript 中,nullundefined 都表示没有值的情况,但它们具有不同的含义。

  • undefined 表示声明了一个变量,但没有给它赋值。未初始化的变量默认值为 undefined。当函数没有返回值时,返回值为 undefined。在访问对象上不存在的属性时,返回值也是 undefined
  • null 表示一个值被明确地定义为空值。可以将其赋给任何类型的变量,如对象、数组等,表示这个变量不引用任何对象或数组元素。

在使用时,通常情况下:

  • 如果想要表示一个变量没有被赋值,就可以将其初始化为 undefined
  • 如果想要表示一个变量明确地为空,就可以将其赋值为 null

需要注意的是,在比较时,nullundefined 的比较需要使用严格相等运算符(===),因为它们有不同的数据类型。例如,undefined == null 返回 true,但是 undefined === null 返回 false

所以把一个对象赋值给一个变量的时候,这个时候赋值的其实是地址,所以当我们进行数据修改的时候,就会修改存放在地址上的值,也就导致两个变量的值都发生改变

# 值传递和引用传递

当给函数传递基本数据类型的参数时,函数内部修改该参数不会导致参数本身发生变化,这就是按值传递的意思。那么当参数类型为引用类型的时候,就是按引用传递参数了吗?并不是!!当函数参数是引用类型时,我们同样将参数复制了一个副本到局部变量,只不过复制的这个副本是指向堆内存中的地址而已,我们在函数内部对对象的属性进行操作,实际上和外部变量指向堆内存中的值相同,但是这并不代表着引用传递。

let obj = {};
function changeValue(obj){
  obj.name = 'linjiaheng';
  obj = {name:'kobe'};
}
changeValue(obj);
console.log(obj.name); // linjiaheng
1
2
3
4
5
6
7

所以记住:函数参数传递的并不是变量的引用,而是变量拷贝的副本,当变量是原始类型时,这个副本就是值本身,当变量是引用类型时,这个副本是指向堆内存的地址。

函数传递对象参数其实是相当于接受实参的隐式引用,在函数内部修改传进去的对象属性,会导致传入的对象变化。

function test(person) {
  person.age = 26
  person = {
    name: 'yyy',
    age: 30
  }

  return person
}
const p1 = {
  name: 'yck',
  age: 25
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

img

TIP

实参不是对象时就是按值传递,函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。

  • 函数参数为基本数据类型时,函数体内复制了一份参数值,任何操作都不会影响原参数的实际值。
  • 函数参数是引用类型时,当在函数体内修改这个值的某个属性值时,将会对原来的参数进行修改。
  • 函数参数是引用类型时,如果直接修改这个值的引用地址。比如person={...}新的属性值赋予给对象。则相对于在函数体内新建了一个引用,任何操作都不会影响到原来参数的实际值。

# 判断数据类型

  • typeofnull、函数、数组都可以判断为对象object,所以不能准确判断变量到底是什么类型。
var str1 = 123
console.log(typeof str1 === 'number') // true
console.log(typeof Array) // 'function'
console.log(typeof {}) // 'object'
console.log(typeof function name(){console.log(222)}) // 'function'

// function /object
function fun1(){};
const fun2 = function(){};
const fun3 = new Function('name','console.log(name)');

const obj1 = {};
const obj2 = new Object();
const obj3 = new fun1();
const obj4 = new new Function();

console.log(typeof Object);//function
console.log(typeof Function);//function
console.log(typeof fun1);//function
console.log(typeof fun2);//function
console.log(typeof fun3);//function
console.log(typeof obj1);//object
console.log(typeof obj2);//object
console.log(typeof obj3);//object
console.log(typeof obj4);//object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

img

所有 Function 的实例都是函数对象,其他的均为普通对象,其中包括 Function 实例的实例。

  • instanceof:判断的原理是根据原型链
function Person() {

}
let p1 = new Person()
console.log(p1 instanceof Person) // true
console.log(p1 instanceof Object) // true
console.log(Person instanceof Object) // true
console.log(Person instanceof Function) // true
console.log(Object instanceof Function) // true
1
2
3
4
5
6
7
8
9
var str = 'hello world'
str instanceof String // false

var str1 = new String('hello world')
str1 instanceof String // true
1
2
3
4
5

对于原始数据类型要直接使用instanceof是行不通的,为啥第二个例子就可以呢?是因为这个时候已经使用new String强制转换成String对象类型。instanceof后面跟着是构造函数,typeof和基本类型比较

class PrimitiveString {
  static [Symbol.hasInstance](x) {
    return typeof x === 'string'
  }
}
console.log('hello world' instanceof PrimitiveString) // true
1
2
3
4
5
6
  • toString:最完美的方案
function checkType(payload) {
  switch(Object.prototype.toString.call(payload)) {
    case '[object Null]': console.log('null')
      break;
    case '[object Number]': console.log('number')
      break;
    case '[object String]': console.log('string')
      break;
    case '[object Undefined]': console.log('undefined')
      break;
    case '[object Boolean]': console.log('boolean')
      break;
    case '[object Array]': console.log('Array')
      break;
    case '[object Object]': console.log('Object')
      break;
    case '[object Number]': console.log('NaN')
      break;
    // 还有其他类型Map Symbol等不一一列举
  }
}
checkType({a:2}) // Object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

TIP

同样是检测对象 obj 调用 toString 方法,obj.toString() 的结果和 Object.prototype.toString.call(obj) 的结果不一样,这是为什么?

这是因为 toStringObject 的原型方法,而 Arrayfunction 等类型作为 Object 的实例,都重写了 toString 方法。。不同的对象类型调用toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法( function 类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串…),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString() 不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 toString 方法。

  • constructor:原型prototype的一个属性,nullundefined没有这个属性所以无法判断类型。

有两个作用,一是判断数据的类型,二是对象实例通过对象访问它的构造函数。需要注意,如果创建一个对象来改变它的原型,就不能判断数据类型了。

var o = ''
console.log(o.constructor === String) // true
console.log(o.constructor.name) // String

// 改变constructor
function Fn(){};

Fn.prototype = new Array();

var f = new Fn();
console.log(f.constructor === Fn); // false
console.log(f.constructor === Array); // true;
1
2
3
4
5
6
7
8
9
10
11
12

# 类型转换

  • 转换的情况:
    • 转换为布尔值
    • 转换为数字
    • 转换为字符串
  1. 转换为布尔值

在条件判断时,除了undefinednullfalseNaN,'',0,-00nbigint类型)其他都转为true 在利用Boolean()方法强类型转换时,只有false,空字符串,0/NaNnullundefined会转换为false。其余都转为true

Boolean([]) // true
Boolean({}) // true
1
2
  1. Number类型
  • 八进制:前面为0,如070(八进制56)。如果其余数值超过7,则忽略前面的0直接解析为十进制数值
console.log(079) // 79
1
  • 十六进制:前面跟着0x其余为(0~9及A~F)
  • 可以使用isFinite()函数判断参数位于最小Number.MIN_VALUE和最大值MAX_VALUE之间,是则返回true

数值转换

    1. Number()函数
    • Boolean--> true转为1false转为0
    • 数值则简单传入传出
    • null--> 0
    • undefined--> NaN
    • 字符串
      • 字符串只包含数字(包括正负,八进制)全部转为十进制的
      • 字符串包含浮点,则转为对应的浮点值,同样忽略八进制前导0
      • 字符串包含有效的十六进制则转为相对应的十进制数值
      • 空字符串则转为0
      • 其他情况则转为NaN
    • 对象: 调用对象的valueOf()(返回对象的原始值),然后依照规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再次依照前面的规则转换返回的字符串值。
    let obj={
        value:'你好啊',
        num:2,
        toString:function(){
          return this.value
        },
        valueOf:function(){
          return this.num
        },
    }
    console.log(obj+'明天')  //2明天
    console.log(obj+1)    // 3
    console.log(String(obj))   // 你好啊
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    当对象进行类型转换时:1.首先调用valueOf,如果执行结果是原始值,返回,如果不是下一步。2.其次调用toString,如果执行结果是原始值,返回,如果不是,报错。特殊情况:当使用显示类型转换成String时,执行顺序则是先调用toString,其次调用valueOf

    TIP

    1. Number(['1'])转为1。先调用valueOf转成['1']还不是原始值,则调用toString转成'1'
    2. 'true' == true //false Number('true')->NaN Number(true)->1
    3. toFixed(num):toFixed() 方法可把 Number 四舍五入为指定小数位数的数字; 参数num: 代表小数位数。返回的是字符串格式了
    4. '' == nullfalse{}+10返回1010+{}返回10[object Object]
    1. parseInt()函数,解析字符串,并返回一个整数
    • 忽略字符串前面的空格,直到找到第一个非空格字符。如果第一个字符非数值或者负号,就会返回NaN也就是说空字符串转为NaN,而不是0
    • 如果第一个字符为数值,则继续解析后面的字符,直到遇到非数值字符。
    parseInt('123blue') // 123
    parseInt('1231.11') // 1231 小数点为非数值字符
    parseInt('abc123') // NaN
    
    1
    2
    3
    • 能够有效识别前导,0x识别为十六进制然后转为十进制。八进制的时候则忽略前面的0
    parseInt('0x11') // 17
    parseInt('011') // 11 而不是9 IE下才是9,十进制
    parseInt(011) // 这才是9,011解析成字符串'011'识别为八进制
    parseInt('011',2) // 3
    parseInt(011,2) // 011->9,二进制只有0/1,所以NaN
    parseInt(111,2) // 7
    parseInt(8,3) // 表示在基数3中“解析"8"。但是,在基体3中,1位数字只是0,1和2。由于没有有效字符,返回NaN 。
    parseInt(16, 3) //要求它在基数3中解析"16"。因为它可以解析1,它会解析,然后它在6处停止,因为它无法解析它。 所以它返回1 。
    parseInt(020,10) // 16 即parseInt('020',8)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    没有忽视0真正转为八进制是ES3下,忽略0的是ES5。ES5的引擎下该函数不具解析八进制的能力了。所以该函数才提供了第二个参数转换时使用的基数

    var num = parseInt('010',8)
    console.log(num) // 8
    
    1
    2

    TIP

    • 如果指定基数为16,则字符串不用带0x如果参数 radix 小于2或者大于 36,并且第一个参数数值不能大于第二个基数,这样才能有效进行进制转换,否则 parseInt() 将返回 NaN
    parseInt('111', 0) //111 返回第一个参数的number类型
    
    1
    • 如果忽略或者为0,那么数字使用十进制表示的。
    parseInt('111') // 111
    
    1
    • 如果数字是以0x0X开头的那么数字使用十六进制表示。
    • 待考察
    parseInt(0.5); // -> 0
    parseInt(0.05); // -> 0
    parseInt(0.005); // -> 0
    parseInt(0.0005); // -> 0
    parseInt(0.00005); // -> 0
    parseInt(0.000005); // -> 0
    parseInt(0.0000005); // -> 5
    
    1
    2
    3
    4
    5
    6
    7
    1. parseFloat() 解析一个字符串并返回一个浮点数
    • 从第一个字符解析,遇到无效的浮点数字字符就终止解析。如第二个小数点。
    • 始终忽略前导0
    • 只解析十进制,因此没有第二个参数作为基数。
    • 十六进制格式的字符串始终解析成0
    parseFloat('0x11') // 0
    
    1
    • 字符串如果为可以解析成整数的,则会返回整数
    parseFloat('123blue') // 123
    
    parseFloat('qwar4s2') // NaN
    
    1
    2
    3
  1. String类型

转为字符串

  1. toString()nullundefined没有这个方法)

该方法属于对象Object方法,由于所有对象都继承了Object的对象实例。所以Array/Boolean/Date/Error/Function/Number/String都能调用该方法。调用数值的该方法时,传入一个参数作为基数,可以返回规定进制的字符串值。没有参数就默认十进制呗

var num = 10
num.toString(16); // 'a' a的十六进制为10
num.toString(8); // '12'

// 引擎中这样执行,因为num基本数据类型没有toString方法
var num = new Number(10);
num.toString(); // '10'
num = null;
1
2
3
4
5
6
7
8

上述代码是JS引擎自动执行的,你无法访问num对象,它只存在于代码的执行瞬间,然后立即销毁。

  1. nullundefined因为没有相应对构造函数,所以没有toString()方法,则用String()方法转为字符串
console.log(String(null)) // 'null'
1
  1. 在js中数字后面的'.'操作符意义不确定。因为可能是一个浮点数的标志也可能是取一个对象的属性的运算符。但是js的解释器把它当成了浮点数的标志。
console.log(10.toString())// Uncaught SyntaxErro: Invalid or unexpected token
console.log(10..toString()) // '10'
console.log((10.).toString) // '10'
1
2
3

# 运算符

  1. ++--

递增和递减操作符,放在数值前面后面结果是截然不同的。放前面是:先进行递增或递减再进行运算;放后面是先进行运算,再进行递增或递减。

var age = 22
var herAge = 23
// var all = ++age + herAge
// console.log(all) // 46
var twoAll = age-- + herAge
console.log(twoAll) // 45
console.log(age) // 21
1
2
3
4
5
6
7
  • 递增递减运算符用在布尔值,字符串,浮点数值和对象的情况:
    • 包含有效数值的字符串-->转为数字值-->再递增或递减
    • 不包含有效数值-->NaN
    • 布尔值-->1/0-->再递增或递减
    • 浮点-->执行加减1操作
    • 对象--> 调用对象valueOf()方法-->进行转换,若为NaN;则利用toString()方法继续。解析对象原始值转换
  1. +特别情况
  • 两方都为字符串,则形成字符串拼接
  • 运算中其中一方为字符串,那么就会把另外一方也转换为字符串
  • 如果一方是对象则调用valueOf/toString方法取得值,将其转换为基本数据类型再进行字符串拼接
  • 其他情况都会转为number类型,undefined转为NaN
  • 如果是Infinity+Infinity则结果为Infinity
  • 如果是-Infinity + (-Infinity)则结果是-Infinity
  • 如果是Infinity + (-Infinity)则为NaN
4+[1,2,3] // 41,2,3
4+{} // 4[object Object]
1
2
  • 还得注意这种情况:
'a'+ +'c' // aNaN

undefined+10 //NaN undefined转为NaN

console.log({}+true) // [object Object]true
{}+true // 1 浏览器控制台

" \t \n" - 2  // -2 

'1'+1n // '11' 比较特殊字符串和BigInt相加,BigInt转换为字符串
1+1n // 错误,不能把数值和BigInt相加
1
2
3
4
5
6
7
8
9
10
11

字符串转换为数字时,会忽略字符串的开头和结尾处的空格字符。在这里,整个字符串由空格字符组成,包括 \t、\n以及它们之间的“常规”空格。因此,类似于空字符串,所以会变为 0

  • 一元操作符在数值前相当于正负,若在对象,字符串,布尔值,浮点数前就会进行数值转换操作,规则和上述一样。
  1. 除了加法运算符,其他运算符只要其中一方为数字,另外一方则转为数字。
  2. 乘性运算符:乘法,除法,求模。

参与乘性计算的某个操作符不是数值,则后台利用Number()转型函数转换为数值。

00除结果为NaN,7/0Infinity,Math.pow(10, 1000)也为Infinity

求模中被除数为0,则结果为0

  1. 比较运算符
  • 如果一个操作数为数值,则将另外一个转换为数值
  • 如果一个操作数为布尔值,则也转换为数值
  1. 相等操作符
  • null == undefined。这两个基本类型数据不能转换为其他值,即如果其中一个操作值是null/undefined,那么另一个操作符必须为null/undefined,才会返回true,否则都是false
  • 两个操作数都为对象,就要看两个对象是否同一个。(地址是否指向同一个)
  • 判断两者类型是否为string/number,是的话就会将字符串转换为number
  • 判断其中一方是否为boolean,是的话就会把boolean转为number再进行判断。
  • 判断其中一方是否为object且另一方为string/number/symbol,是的话就会把object转为原始类型再进行判断。
  • 如果其中一个操作数为Symbol类,那么返回false
[] == ![] // true 都转为0。后者![]是先布尔类型转换,转换为false,然后数值转换为0
'' == !'' // false 前者转为0后者为1

var a = [0];
if (a) {
  console.log(a == true); // false
} else {
  console.log(a);
}
1
2
3
4
5
6
7
8
9
  1. 逗号操作符
  • 用于声明多个变量
  • 用于赋值, 逗号操作符只返回最后一个操作符的值
var num = (1,2,3,4) // num值为4 最后一个
1
  • 尾逗号
[,] + [,]; // -> ""
[] + [] === [,] + [,]; // -> true
[,,,] + [,,,]; // -> ",,,,"
([,,,] + [,,,]).length === [,,,,].length; // -> true
1
2
3
4

TIP

不合法的尾后逗号

  • 仅仅包含逗号的函数参数定义或者函数调用会
  • 当使用剩余参数的时候,并不支持尾后逗号
  • 解构时使用剩余参数也会抛出错误
  • JSON格式
function f(,) {} // SyntaxError: missing formal parameter
(,) => {};       // SyntaxError: expected expression, got ','
f(,)             // SyntaxError: expected expression, got ','

function f(...p,) {} // SyntaxError: parameter after rest parameter
(...p,) => {}        // SyntaxError: expected closing parenthesis, got ','

var [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma

//JSON不允许尾后逗号
JSON.parse('[1, 2, 3, 4, ]');
JSON.parse('{"foo" : 1, }');
// SyntaxError JSON.parse: unexpected character
// at line 1 column 14 of the JSON data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. 布尔操作符
  • 如果操作数为一个对象(空数组也是),则逻辑非结果为false
  • 逻辑与在有一个操作数不是布尔值的情况下
    • 第一个操作数是对象,则返回第二个操作数;
    • 第二个操作数为对象,只有第一个操作数为true时,才会返回该操作数
    • 如果两个都为对象,则返回第二个
    • 如果有一个操作数为null,NaN,undefined,则返回null,NaN,undefined
  • 逻辑或在有一个操作数不是布尔值的情况下
    • 如果第一个操作数为对象,则返回第一个操作数
    • 如果第一个操作数为false,返回第二个操作数
    • 如果两个操作数都为对象,返回第一个操作数
    • 如果两个操作数都为null,NaN,undefined,则返回null,NaN,undefined
  1. ??操作符(零合并操作符)

如果第一个参数不是null/undefined,这个运算符返回第一个参数,否则返回第二个。

null??5 // 5
3??5 //3
1
2
  1. ??=(逻辑空值赋值运算符)
var x = null
var y = 5

console.log(x ??= y) 
// => 5
console.log(x = (x ?? y)) 
// => 5
1
2
3
4
5
6
7
  1. ?.可选链操作符

可选的链式操作符?.允许开发人员读取深嵌在对象链中的属性值,而不必显式验证每个引用。当一个引用为空时,表达式停止计算并返回一个未定义的值。

var travelPlans  = {
  destination: 'DC',
  monday: {
    location: 'National Mall',
    budget: 200
  }
};
const tuesdayPlans = travelPlans.tuesday?.location;
console.log(tuesdayPlans) // => undefined
1
2
3
4
5
6
7
8
9

TIP

现在就可以利用可选链操作符解决cannot read property of undefined的一个常见错误。

传统解决方法就是:

  • 通过&&短路运算符来进行嗅探
  • 通过||设置默认保底值
  • 利用try...catch
var result
try {
    result = obj.user.posts[0].comments
} catch {
    result = null
}
1
2
3
4
5
6

# 运算符优先级

关联性决定了拥有相同优先级的运算符的执行顺序。

a OP b OP c;
1

左关联(左到右)相当于把左边的子表达式加上小括号(a OP b) OP c,右关联(右到左)相当于a OP (b OP c)。赋值运算符是右关联的。

a = b =5
1

结果 ab 的值都会成为5。这是因为赋值运算符的返回结果就是赋值运算符右边的那个值,具体过程是:b被赋值为5,然后a也被赋值为b=5的返回值,也就是5

下表将所有运算符优先级从高(21)到低0 img

img

img

img

img

img

# 基本包装类型

实际上每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。

var s1 = 'some text'
var s2 = s1.substring(2)
1
2

TIP

引用类型与基本包装类型主要区别是对象生存期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一代代码的执行瞬间,然后立即销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。

  1. 对基本包装类型的实例调用typeof会返回object,而且所有基本包装类型的对象在转换布尔值类型都为true
  2. Object构造函数会像工厂函数一样,创造基本包装类型的实例。也可以使用new调用基本包装类型(不过不推荐这种方法)。
var obj = new Object('test')
console.log(obj instanceof String) // true

var value = '25'
var obj2 = new Number(value)
console.log(typeof obj2) // object
1
2
3
4
5
6

# String类型

  • 基于子字符串创建新字符串的方法:第一个参数指定子字符串开始位置;第二个参数表示哪里截至(可选)。substr()方法第二个参数表示返回字符串字符的个数。

    • slice
    • substring
    • substr 传递负值参数时。slice会将负值和长度相加,substr会将第一个负值和长度相加,第二个转为0substring会将所有负值参数都转为0
  • trim():删除前后空格符,返回一个字符串副本。trimLeft/trimRight

  • charCodeAt():获取字符编码。

# 位操作符

位操作符用于最基本的层次上,即按内存中表示数值的位来操作数值。对于有符号的整数,32位中的前31位用于表示整数的值。第32位用于表示数值的符号:0表示正数,1表示负数。负数同样用二进制存储,但使用的格式是二进制补码。

  • 计算一个数的二进制补码:
    • 求这个数值绝对值的二进制码
    • 求二进制反码,即0替换为11替换为0
    • 得到的二进制反码加1

~:按位非(本质:操作数的负值减1&:按位与

第一个数值位 第二个数值位 结果
1 1 1
1 0 0
0 1 0
0 0 0

|:按位或:

第一个数值位 第二个数值位 结果
1 1 1
1 0 1
0 1 1
0 0 0

^:按位异或:

第一个数值位 第二个数值位 结果
1 1 0
1 0 1
0 1 1
0 0 0

和按位或的区别是两位操作数对应的数值位的数值要不相同才为1,相同则结果为0

<<:左移

左移时右边会出现空位就用0来填充,以便得到的结果是完整的32位二进制数。左移不会影响符号位

>>:有符号的右移

这个操作符将数值向右移动,但保留符号位。有符号位的右移操作和左移操作恰好相反。右移左边出现的空位会用符号位上的值来填充

>>>:无符号右移

32位都向右移动。对正数来说该操作和有符号右移相同。对负数来说无符号右移是用0来填充空位了。其次,无符号右移操作符会把负数的二进制码当成正数的二进制码。而且,由于负数以其绝对值的二进制补码形式表示,因此就会导致无符号右移后结果非常之大。