JavaScript 变量
来源:互联网 发布:zip如何安装mysql 编辑:程序博客网 时间:2024/06/13 17:41
JavaScript 变量
JavaScript 被称为弱类型语言,是指它的变量是无类型的,可以用来存储各种类型的 JavaScript 值, 同时它能够在以后任意时刻被重新赋值其它类型。
什么是变量(variable)
变量是内存里用来存储值的空间。每个变量都有唯一的内存地址。
基本类型变量
JavaScript 的值分为两大类,基本类型(primitive type) 和 对象(object)。这里的基本类型变量就是指存储基本类型值的变量, 注意变量是无类型的,这里给它杜撰了一个类别是为了避免歧义。
基本类型变量,其内存地址保存的值就是其被赋值时的值。基本类型的值是不可变的,要想改变基本类型变量中的值,实际上做的是给这个变量重新赋值:
var a = 1;// want to change it to another value? reassign it thena = 10;
对象变量
对象,也称为复合类型或引用类型,在这里使用引用类型的术语更形象一些。同上,对象变量就是引用对象值的变量。
注意我这没有定义它为存储对象值的变量,而是引用,因为实际上对象变量的内存地址存储的是能够引用这个对象值的“指针”。
对象的值是可变的,但是改变对象的值并不改变对象变量的值。这里再明确一点,对象变量保存的是指向对象值所在内存地址的信息,调用变量时可以通过这个信息获取这个对象值,我们这里可以假设对象变量的值是“地址类型”的值。同时还要明白一点,这里说的改变对象的值不是给对象变量重新赋值。
var objA = { x: 1, y: 2};var objB = objA;objA.x = 100;console.log(objA === objB); // still true
为什么会这样?因为我们使用 objA 和 objB 的时候,它们看似是对象,实则是“地址类型”的值,它们自身是两个不同内存空间保存着相同的“地址类型”值,js engine 是这样做的:
// what js engine does, in pseudo codevar objA = addressA -> {x: 1, y: 2}var objB = objA = addressA -> {x: 1, y: 2}// addressA, a mimic value type "地址类型"
这里,“地址类型”是杜撰的,但理解它很重要。
基本类型变量 vs 对象变量
相同的是,它们都是变量,并且都拥有唯一的内存地址,它们的内存地址不共享。因为,如果地址共享,以下情况不会发生:
// same, about reassignment// for primitivevar a = 1;var b = a;a = 10;console.log(a === b); // falseconsole.log(a); // 10console.log(b); // 1// for objectvar c = {x: 1, y: 2};var d = c;c = {x: 1, y: 2}; // 这里,有一个右值的概念,就是取值,直接量会在内存中新建一个空间来存储这个对象值,也就有了对应的新的“地址类型”值。// 因此,变量的内存地址没变(这一点是我目前的理解),而其存储的”地址类型”的值已变。console.log(c === d); // falseconsole.log(c); // {x: 1, y: 2}console.log(d); // {x: 1, y: 2}// 再说一次,它们看似对象且一样,实际“地址类型”的值不一样
不同的是,额,说到这,它们其实没有不同。任何变量之间都没有本质的不同,它们惟一的区别就是其内存中保存的值不同而已。我这里想说的不同,其实是变量重新赋值和值的改变之间的不同:
// not same, primitive type can't do this, object can, which is changing valuevar a = {x: 1, y: 2};var b = a;a.x = 100;console.log(a === b); // truea = {x: 100, y: 2};console.log(a === b); // false// nodejsexports = someValue; // error, why?module.exports = someValue; // no error// prototype inheritfunction F() {};F.prototype = {/* some properties */};var f = new F();console.log(f.constructor === Object); // true, why not F ?
通过这些说明,我们应该能理解变量的一些本质。总结一下,变量具有唯一且不变的内存地址,要么直接存储基本类型的值,要么间接存储对象的值(实际存储“地址类型”的值, 它可以引用对象值)。
变量申明(variable declaration)
或者叫做变量定义,就是新建一个保存值的符号。
使用 var
这种方式最常用,使用 var 申明的变量会有以下特性:
自动成为全局对象的不可配置属性:
// no strict modevar a = 1;console.log(Object.getOwnPropertyDescriptor(this, "a"));// { value: 1, writable: true, enumerable: true, configurable: false }
变量提升(hoisting), 只要申明了变量,任何情况下变量定义被提升到当前作用域顶部,被显示赋值之前拥有默认值 undefined
'use strict'; // no error even in strict modevar condition = Math.random() > 0.5 ? true : false;console.log(a); // undefined /* 1. no error */if (condition) { var a = 1; // repeated declaration /* 2. no error again */} else { var a = 2;}
可重复申明,上例已说明。
严格模式中规定变量必须先申明再赋值, 非严格模式未申明变量成为全局对象可配置属性, ES 标准规定(用了 must 一词)我们总是先申明变量再使用它。
function test() { 'use strict'; a = 10; console.log(a); // error}// no strict modefunction f() { a = 10;}f();console.log(Object.getOwnPropertyDescriptor(this, a));// { value: 10, writable: true, enumerable: true, configurable: true }
使用 let
这是 ES6 新增的变量申明关键字。它的作用基本上跟 var 一样,都申明了一个变量,但是它有以下不同:
let 申明的变量不会成为全局对象的属性:
// no strict mode , for exmaple description purposevar a = 1;let b = 1;"a" in this; // true"b" in this; // false
没有变量提升,有“暂时性死区”(TDZ, temporary dead zone), 申明前使用报错。
console.log(a); // undefinedconsole.log(b); // errorvar a = 1;let b = 1;// another one for TDZfunction test(a = b, b) {}test(undefined, 1); // error
同一作用域内不可重复申明相同变量。
var a = 1;let a = 2; // error// anotherlet a = 1;let a = 2; // error
上面3条都是对使用 var 申明变量副作用的限制,其实 let 更重要的特性是它的块级作用域, 这个块级作用域分为两种,一种是语句块作用域,一种是语句作用域:
// 语句块作用域{ let a = 1; console.log(a); // 1}console.log(a); // error// 语句作用域for (let a = 0; a < 10; a += 1) { console.log(a); // 0, 1, 2 ... -> 9}// 这里, let 是在 {} 语句块之外定义的,那么它应该是个全局变量吧?console.log(a); // error /* wtf? */// 那么,for 语句头部定义的变量必须得是局部变量了, 那作用域是 {} ?for (let a = 1; a < 10; a += 1 + b) { let b = 1; console.log(a); // error /* wtf? */}// 那么,for 语句头部定义的变量就是作用域在头部内的变量,我称之为语句作用域。// {} 内部可以读取头部作用域的变量,反之不然, 这就相当于 header 是 body 的外部作用域, 这很奇妙
使用 const
const 也是 ES6 新增的关键字,使用 const 申明的变量所具有的特性几乎跟 let 申明的变量一样,除了以下不同:
const 申明的变量必须初始化,即申明时赋值。
const a; // error// anotherconst a = 1; // no error
const 申明的变量不能被重新赋值。回忆前面提到的重新赋值和值的改变之间的区别。
const a = 1;a = 2;const b = {x: 1};b.x = 10; // no error /* 再再说一次,b 的“地址类型”值没变,变的是其引用的对象值, remember that? */
块级作用域
块级作用域和函数作用域作为局部作用域的特性是一致的, 都遵循词法作用域。块级作用域相当于立即执行的函数表达式(IIFE), 因此其内部嵌套的函数拥有闭包的一切特性。
// varvar funcs = [];for (var i = 0; i < 10; i += 1) { funcs.push((function(value) { return function() { return value; } })(i));}for (let f of funcs) { console.log(f()); // 1, 2, ... -> 9}// letvar funcs = [];for (let i = 0; i < 10; i += 1) { funcs.push(function() { return i; });}for (let f of funcs) { console.log(f()); // 0, 1, 2, ... -> 9}
- JavaScript变量
- JavaScript 变量
- JavaScript 变量
- JavaScript 变量
- JavaScript 变量
- javascript变量
- javascript 变量
- JavaScript 变量
- JavaScript 变量
- JavaScript 变量
- JavaScript----变量
- javascript变量
- JavaScript 变量
- javascript变量
- Javascript变量
- JavaScript 变量
- javaScript 变量
- JavaScript-变量
- 软件概要设计
- 【unity3D·MMD】镜头数据vmd格式的读取插件实现
- 使用Java向MySQL插入datetime,防止时分秒信息丢失
- 批量删除Windows7中隧道适配器的方法
- JAVA toBinaryString()方法 toCharArray stringBuilder的用法
- JavaScript 变量
- Unity3D说明文档翻译-Network Emulation
- jsp, servlet之helloworld
- codeforces 676b 模拟
- OpenGL学习笔记(八)
- 软件系统架构设计
- Unity3D说明文档翻译-Audio Manager
- 蓝桥 密文搜索
- php 去掉小数末尾的0