3.12 空值

空值既简单又复杂

Ruby Javascript Javascript Go Lua
空值 nil undefined null nil nil
预定义值/关键字 关键字 预定义值 关键字 预定义值 关键字

关键字不允许作为赋值的左值, 而预定义值是可以被重新赋值的.


1. Ruby 的 nil

Ruby 中一切皆为对象, nil也不例外, nil是NilClass唯一的实例:

nil.class # NilClass

nil 和布尔值false不等:

nil == false #false

不过当Ruby需要一个布尔值是, nil表现和false一致, 都是为假

对象判断是否是真正的nil有2种方式:

o == nil

或者

o.nil?


2. Javascript 的 undefined 和 null

  • null 和 undefined 是语言设计上的历史包袱, 时至今日, null和undefined基本是同义的,使用上有一些细微的差别:

    null:

    (1) 作为函数的参数,表示该函数的参数不是对象。
    (2) 作为对象原型链的终点。

    undefined:

    (1)变量被声明了,但没有赋值时,就等于undefined。
    (2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
    (3)对象没有赋值的属性,该属性的值为undefined。
    (4)函数没有返回值时,默认返回undefined

  • undefined 并不是一个字面量, 不是保留字, 只是一个值, 因此undefined其实可以作为变量进行赋值, 这是引起某些bug的潜在原因

    在某些jQuery代码中有这种写法:

    ;(function(window,undefined){
    
    })(window)
    

    其中一个原因就是防止全局的undefined变量被修改成其他值

  • null 是一个字面量, 是个保留字,你不能把null作为变量名, 不能被赋值

  • undefined和null在逻辑运算语句中,都会被自动转为false

  • 不严格比较==, 会报告两者相等: undefined == null #true

  • typeof

    • typeof undefined => 'undefined'
    • typeof null => 'object', null 在语义上表示不是一个对象, null表示一个空对象指针, 这也是为什么null的typeof返回「object」.

      如果需要判断一个值是不是null, 可以直接用=== null 进行判断


3. Go 中的nil

nil is a predeclared identifier representing the zero value for a pointer, channel, func, interface, map, or slice type

  • 无类型(untyped)的预定义值
  • 用于代表pointer, channel, func, interface, map, slice的零值, 因此也只能用于这6种类型

nil 是无类型的预定义值, 而不是关键字, 预定义值在语法上可以被重新赋值:

nil := 123 //ok

在golang中,nil只能赋值给pointer, channel, func, interface, map, slice类型的变量. 如果未遵循这个规则,则会引发panic

以上六种类型nil值的含义如下:

pointer

空指针, 指向nil, 亦可以说是什么都没有

nil指针的变量, 仍然可以调用其类型方法集中的方法, 以下代码可以执行成功.

package main

import (
  "fmt"
)

type littleGirl struct {
  Name string
  Age  int
}

func (this *littleGirl) changeName(name string) {
  // this.Name = name // nil 指针不能进行字段操作
  fmt.Println(name)
}
func main() {
  little := &littleGirl{Name: "Rose", Age: 1}
  little = nil
  little.changeName("fox")
  fmt.Println(little)
}

但是以下代码却要报错:

type Point struct {
  X, Y float64
}

func (p *Point) Abs() float64 {
  //panic: runtime error: invalid memory address or nil pointer dereference
  return math.Sqrt(p.X*p.X + p.Y*p.Y)
}

func main() {
  var p *Point
  fmt.Println(p.Abs())
}

If x is nil, an attempt to evaluate *x will cause a run-time panic

new可以初始化指针:

func main() {
    var p *Point = new(Point)
    fmt.Println(p.Abs())
}
slice

一个为nil的slice,除了不能索引外,其他的操作都是可以的.

形如s := make([]byte,5) 的切片内部构造如下:

[]byte
ptr *elem //ptr指向一个底层数组它有五个元素[0,0,0,0,0]
len 0
cap 0

var s []byte的切片将初始化为零值nil, 内部结构如下:

[]byte
ptr nil //空指针
len 0
cap 0

map, channel, func

非零值的map, channel, func, 内部有个ptr 指向具体内部实现, 零值的map, channel, func内部指针是空指针:

ptr nil

map

对于nil的map,可以简单把它看成是一个只读的map,不能进行写操作,否则就会panic

channel

关闭一个nil的channel会导致程序panic

nil的channel是永远阻塞的

interface

interface并不是一个指针,它的底层实现由两部分组成,一个是类型,一个值,也就是类似于:(Type, Value)

只有当类型和值都是nil的时候,才等于nil

(type, value) 输出结果是 <nil> <invalid reflect.Value>, 可以看到是无类型(untyped)

划重点

  • slice: 能写不能读
  • map: 能读不能写
  • chan: 不能读不能写(阻塞但不报错)
  • pointer: 可以调用方法但是不能访问内容(字段等)

4. Lua 的nil

给变量赋值nil将回收此变量内存, 删除变量, 以及删除map的键值对, 都可以通过赋值nil实现.

nil只等于自己本身


参考资料: Go: Understanding Nil

results matching ""

    No results matching ""