til
  • README
  • Software Development Roles
  • solid
  • README
    • service-worker
  • docker
    • arg
    • 更新docker版本
  • editor
    • vscode
    • Creating a VS Code Theme
  • english
    • words
  • front-end
    • ==
    • ECMAScript
    • IIFE
    • Label
    • basic
    • html.js.css渲染顺序
    • npm-vs-yarn
    • obj-delete-key-value
    • react
    • split-join-and-replace
    • video
    • 前端自检清单
    • 递归及去重
    • css
      • css换肤
      • flex
      • list
      • nth-child和nth-of-type区别
      • padding
      • position
      • 层叠上下文
      • 层叠样式(+)
      • 正方形
      • 语义化标签
    • dom
      • DOCTYPE
      • HEAD
      • 修改document
      • 自定义表单验证
    • electron
      • basic
    • es6
      • basic-type
      • basic
      • prototype-example
      • defineProperty
      • understanding-es6
        • 0.introduction
        • Appendix A: Smaller Changes
        • Appendix B: Understanding ES7
        • Block-Binding
        • Proxies&Reflection
        • class
        • 解构赋值
        • function
        • improved-array
        • iterators&generators
        • modules
        • object
        • promise
        • Map&Set
        • symbol
    • images
      • 前端角度看图片
    • interview_case
      • lexical_scope
      • redux和localstroage存储位置
    • javascript
      • fuck-the-js
      • js-engine-work
      • js原生操作dom
      • what-is-function-program
      • 执行上下文
      • articles
        • JavaScript中使用函数组合
        • JavaScript中的依赖注入
        • JavaScript作用域链中的标识符解析和闭包
        • JavaScript是何如工作的--概述
        • JavaScript深拷贝
        • JavaScript的全局变量是如何工作的
        • js继承常见的误解
        • node12&chrome中7个新的提案功能
        • 你真的懂JavaScript吗
      • date
        • index
      • engines
        • basic
        • JavaScript引擎基础:外形和内联缓存
        • v8中推测性优化的介绍
        • 优化原型
        • 更快的异步功能和promise
      • events
        • baisc
        • 事件冒泡和捕获
        • 定义事件
        • 页面生命周期
      • higher-order-function
        • curry
        • monad
      • module
        • basic
        • main&module
      • objects
        • iterator
        • spread
        • examples
          • iterator
      • performance
        • blocking-css
        • cache
      • prototype
        • Property-Descriptors
        • basic
        • prototype-shadow
      • you-dont-known-js
        • async&performance
          • Chapter 1: Asynchrony: Now & Later
          • Chapter 2: Callbacks
          • Chapter 3: Promises
          • Chapter 4: Generators
        • scope & closures
          • apA
          • apB
          • apC
          • apD
          • chapter1-what-is-scope
          • chapter2-lexical-scope
          • chapter3-function-vs-block-scope
          • chapter4-hoisting
          • chapter5-scope-closure
        • this & object prototypes
          • chapter1-this-or-that
          • chapter2-this-make-sense
          • chapter3-objects
          • chapter4-mixing(up)-class-object
          • chapter5-prototype
          • chapter6-behavior-delegation
        • types&grammer
          • Chapter1-Types
          • Chapter2-Values
          • Chapter3-Natives
          • Chapter4-coercion
          • Chapter5-grammer
        • up & going
          • chapter1-into-programming
          • chapter2-into-javascript
          • chapter3-into-YDKJS
    • mobile
      • iPhone分辨率终极指南
    • npm
      • arguments
      • build
    • react-native
      • prop-methods
    • react
      • PropTypes
      • basic
      • codebase-overview
      • component-element-instance
      • context
      • how-to-known-component-is-func-or-class
      • overview
      • react16.9
      • react18计划
      • react的设计原则
      • reconciliation
      • setState
      • useMemo
      • why-do-we-write-super-props
      • 从头实现一个react
      • concurrent
        • 引入并发模式(仅试验)
      • conf
        • conf-2019
      • events
        • 合成事件概述
      • hooks
        • custom-hook
        • effect-hook
        • hooks-api
        • intro
        • overview
        • rules
        • state-hook
        • hooks-vs-class
          • thinking-in-react-hooks
      • overreact
        • Development模式是如何工作的
        • How-Does-setState-Know-What-to-Do
        • Why-Do-React-Elements-Have-a-$$typeof-Property
        • Why-Do-React-Hooks-Rely-on-Call-Order
        • how-to-known-component-is-func-or-class
        • preparing-tach-talk-motivation
        • react作为ui运行
        • things-i-dont-known-as-2018
        • ui-element-problem-and-build-yourself
        • why-do-we-write-super-props
        • 一份完整的useEffect指南
        • 为什么X不是Hook
        • 函数组件与类有什么不同?
        • 演讲准备2-what-why-how
        • 编写弹性组件
        • 让setInterval在React-Hooks中为声明式
      • practice
        • render
      • react-dom
        • basic
      • react-redux
        • apiv7.1-hooks
        • connect
        • shallow-equal
      • redux
        • applyMiddleware
        • applyMiddleware2-细节
        • example
    • regex
      • index
    • stories
      • 数组下标
      • 阻止事件冒泡
    • svelte
      • compile-svelte-in-your-head-1
      • compiler-overview
      • parser
        • 写一个解析器-JavaScript的JSON解析器
    • turbopack
      • basic
    • typescript
      • interface和type的区别
    • webpack
      • hash
      • webpack4-for-react
      • webpack4
      • webpack4to5
      • babel
        • babel-parser和acorn的区别
        • babel.7.11
        • family
        • react16.14使用new-transform
        • update-to-7
    • pdf
      • deep-js
        • basic
      • react
        • reintroducing
  • git
    • capital
    • emoji
  • http
    • http2.0
    • response
  • rails
    • api
    • flash
    • middleware-vs-metal
    • model
    • performance
    • routes
    • environment
      • error
    • patterns
      • service
    • sidekiq
      • params
    • deploy
      • capistrano
        • ssh
  • ruby
    • self
    • net
      • http请求携带cookie
  • server
    • ss
    • ssh
    • user
    • crawler
      • puppeteer
    • nginx
      • domain-without-80
      • nginx节省带宽
  • sql
    • rails
    • search
Powered by GitBook
On this page
  • Build-in Types
  • Values as Types
  • undefined vs "undeclared"
  • typeof Undeclared
  • Review
  1. front-end
  2. javascript
  3. you-dont-known-js
  4. types&grammer

Chapter1-Types

Build-in Types

JavaScript定义了七种内置类型:

  • null

  • undefined

  • string

  • number

  • boolean

  • symbol — ES6中添加的

  • object

注意: 除object之外的所有这些类型都称为“primitives(基元, 基础的)”。

typeof运算符检查给定值的类型,并始终返回七个字符串值中的一个 - 令人惊讶的是,与我们刚刚列出的七种内置类型没有精确的一对一匹配。

typeof undefined     === "undefined"; // true
typeof true          === "boolean";   // true
typeof 42            === "number";    // true
typeof "42"          === "string";    // true
typeof { life: 42 }  === "object";    // true

// added in ES6!
typeof Symbol()      === "symbol";    // true

这六种列出的类型具有相应类型的值,并返回相同名称的字符串值,如上所示。symbol是ES6的新数据类型,将在第3章中介绍。

你可能已经注意到,我从上面的列表中排除了null。这是特别的 - 特别是在与typeof运算符结合时它是错误的:

typeof null === "object"; // true

它会很好(并且正确!)如果它返回"null",但JS中的这个bug已持续了将近二十年,并且可能永远不会被修复,因为现有的Web内容过多依赖于其错误的行为,“修复”错误会产生更多“错误”并打破很多网络软件。

如果你想要使用 null 类型来测试 null 值,你需要一个复合条件:

var a = null;

(!a && typeof a === "object"); // true

null是唯一一个原始类型falsy并且typeof检查返回"object"的值。

那么 typeof 可以返回的第七种字符串值是什么?

typeof function a(){ /* .. */ } === "function"; // true

很容易认为function是JS中的顶层内置类型,特别是考虑到typeof运算符的这种行为。但是,如果你阅读规范,你会发现它实际上是对象的“子类型”。具体来说,一个函数被称为“可调用对象” - 一个具有允许调用它的内部[[Call]]属性的对象。

函数实际上是对象的事实是非常有用的。最重要的是,他们可以拥有属性。例如:

function a(b,c) {
    /* .. */
}

函数对象的length属性设置为声明它的形参的数量。

a.length; // 2

由于你使用两个正式命名参数(b和c)声明了该函数,因此“函数的长度”为2。

数组怎么样?它们是JS的原生,它们是特殊类型的吗?

typeof [1,2,3] === "object"; // true

不,他只是对象。将它们视为对象的“子类型”是最合适的(见第3章),在这种情况下,具有数字索引的附加特征(与仅像普通对象一样的字符串键),并维护自动更新的.length属性。

Values as Types

在JavaScript中,变量没有类型 -- 值有类型。 变量可以随时保存任何值。

考虑JS类型的另一种方法是JS没有“类型强制”,因为引擎并不坚持保持他会与开始时的类型一致。变量可以在一个赋值语句中保存一个字符串,并在下一个中保存一个数字,依此类推。

42这个是有固定的number类型,并且这个类型不会被改变。比如另一个值"42"是一个string类型,可以通过强制转换 的过程从number值42创建而来。

如果对变量使用typeof,他不会像表面看起来那样询问“变量的类型是什么?”,因为js的变量是没有类型的。相反,他会询问“变量里值的类型是什么?”

var a = 42;
typeof a; // "number"

a = true;
typeof a; // "boolean"

typeof运算符始终返回一个字符串。所以:

typeof typeof 42; // "string"

首先 typeof 42 返回 "number", 然后 typeof "number" 就是 "string".

undefined vs "undeclared"

当前不存在值的变量,实际上有个undefined值。对这个变量使用typeof,会返回"undefined":

var a;

typeof a; // "undefined"

var b = 42;
var c;

// later
b = c;

typeof b; // "undefined"
typeof c; // "undefined"

大多数的开发者会把“undefined”这个认为是同义词的“undeclared”,这似乎很诱人。但是,在js中,这两个概念完全不同。

"undefined"变量是指他在可访问的范围内已经声明了变量,但是此时还没有其他的值。相比之下,“undeclared”变量是指未在可访问范围内正式声明的变量。

考虑下:

var a;

a; // undefined
b; // ReferenceError: b is not defined

令人讨厌的混淆是浏览器为此条件分配的错误消息。正如你所看到的,消息是“b is not defined”,当然,这就很容易使得混淆“b is undefined”变得非常容易且合理。再次声明,“undefined”和“is not defined”是完全不同的东西。如果浏览器说“b is not found”或“b is not declared”这样的话,可以减少这种困惑!

还有一种与typeof相关的特殊行为, 与未声明的变量有关,甚至进一步加剧了混乱。

var a;

typeof a; // "undefined"

typeof b; // "undefined"

typeof操作符返回"undefined"即使这个变量是"undeclared"(或"not defined")。注意,当我们执行typeof b的时候,即使b是未声明的变量,他也不会抛出错误消息。这是 typeof 的一种特殊的安全防卫行为。

和上面类似地,要是 typeof 与未声明变量一起使用时返回“undeclared”就好了,而不是把结果值与不同的“undefined”情况混在一起。

typeof Undeclared

然而,当在浏览器中处理JavaScript时,这个安全防护是一个有用的功能,其中多个脚本文件可以将变量加载到共享的全局命名空间中。

注意: 许多开发人员认为全局命名空间中永远不应该有任何变量,并且所有内容都应该包含在模块和私有/独立命名空间中。这在理论上是伟大的,但在实践中几乎是不可能的;这还得作为一个目标继续努力!幸运的是,ES6增加了对模块的更好的支持,最终将使这个理论这更加实用。

举个简单的例子,假设程序中有一个“调试模式”,它由一个名为DEBUG的全局变量(标志)控制。在执行调试任务(如将消息记录到控制台)之前,你需要检查是否已声明该变量。顶层的全局变量var DEBUG = true声明仅包含在“debug.js”文件中,你只在开发/测试时加载到浏览器中,而不是在生产中。

但是,你必须注意如何在其余的应用程序代码中检查全局DEBUG变量,以便不抛出ReferenceError。在这种情况下,typeof上的安全防护就显得很友好。

// 这里会抛出错误!
if (DEBUG) {
    console.log( "Debugging is starting" );
}

// 这个是否存在的检查是安全的
if (typeof DEBUG !== "undefined") {
    console.log( "Debugging is starting" );
}

即使你没有处理用户定义的变量(如DEBUG),这种检查也很有用。如果你正在对内置API进行功能检查,你可能会发现在不抛出错误的情况下进行检查很有帮助:

if (typeof atob === "undefined") {
    atob = function() { /*..*/ };
}

注意: 如果你需要为不存在的功能添加一个“polyfill”,你可能想避免使用var去声明atob。如果你在if语句里声明var atob,这个声明会提升(查看本系列的 Scope & Closures)到作用域顶层,即使这个if条件不通过(因为全局的atob已经存在!)。在某些浏览器和某些特殊类型的全局内置变量(通常称为“主机对象”)中,此重复声明可能会引发错误。var可以阻止这个提升的声明。

另一种不带有 typeof 的安全保护特性而对全局变量进行这些检查的方法是,将所有的全局变量作为全局对象的属性来观察,在浏览器中这个全局对象基本上是 window 对象。所以,上面的检查可以这样完成(非常安全):

if (window.DEBUG) {
    // ..
}

if (!window.atob) {
    // ..
}

这与引用未声明的变量不同,如果尝试访问不存在的对象属性(甚至在全局window对象上),不会引发ReferenceError。

另一方面,一些开发人员希望避免使用window手动引用全局变量,特别是当你的代码需要运行在多种 JS 环境中时(例如不仅是在浏览器中,还在服务器端的 node.js 中),全局对象可能并不都是window。

在技术上,typeof的安全保护也很有用,即使你没有使用全局变量。尽管这些情况不常见,而且一些开发人员可能会发现这种设计方法不太理想。想象下你有个工具函数,你希望他人复制黏贴到他们的程序或模块中,你想要检查这个程序(模块中)是否已经包含了某个变量(这样你可以直接使用):

function doSomethingCool() {
    var helper =
        (typeof FeatureXYZ !== "undefined") ?
        FeatureXYZ :
        function() { /*.. 自己默认的功能 ..*/ };

    var val = helper();
    // ..
}

doSomethingCool()对FeatureXYZ变量进行了检测,如果存在,就使用它,如果不存在,就使用自己的。现在,如果有人在他们的模块/程序中包含此实用程序,它会安全地检查是否已定义FeatureXYZ:

// an IIFE (see "Immediately Invoked Function Expressions"
// discussion in the *Scope & Closures* title of this series)
(function(){
    function FeatureXYZ() { /*.. my XYZ feature ..*/ }

    // include `doSomethingCool(..)`
    function doSomethingCool() {
        var helper =
            (typeof FeatureXYZ !== "undefined") ?
            FeatureXYZ :
            function() { /*.. default feature ..*/ };

        var val = helper();
        // ..
    }

    doSomethingCool();
})();

这里,FeatureXYZ并不是一个全局变量,但是我们仍然使用typeof去进行安全的检测保证安全,而且重要的是,这里没有我们可以使用的对象(就像我们使用 window.___ 对全局变量做的那样),所以typeof十分的有帮助。

其他开发人员更喜欢称为“依赖注入”的设计模式,而不是在doSomethingCool()隐式检查FeatureXYZ是否在其外部/周围定义,它需要显式传入依赖,如:

function doSomethingCool(FeatureXYZ) {
    var helper = FeatureXYZ ||
        function() { /*.. default feature ..*/ };

    var val = helper();
    // ..
}

设计此类功能时有很多的选择。这里没有一种模式是“正确的”或“错误的” - 每种方法都有各种权衡。但总的来说,typeof 的未声明安全保护给了我们更多选项,这还是很不错的。

Review

JavaScript有七种内置类型:null, undefined, string, number, boolean, symbol, object。他们可以通过typeof来识别。

变量没有类型,但变量中的值有。这些类型定义值的内有行为。

许多开发人员会认为“undefined”和“undeclared”大致相同,但在JavaScript中,它们却完全不同。undefined可以是一个声明的变量所持有的值,“Undeclared”表示变量还没有被声明。

不幸的是,JavaScript有点混淆了这两个术语,不仅包括它的错误消息(“ReferenceError:a is not defined”),还包括typeof的返回值,对于这两种情况都是"undefined"。

但是,在某些情况下,使用typeof针对未声明变量的类型的安全防护(防止错误)可能会有所帮助。

Previoustypes&grammerNextChapter2-Values

Last updated 6 years ago