博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[js]变量与数据类型篇
阅读量:5052 次
发布时间:2019-06-12

本文共 17751 字,大约阅读时间需要 59 分钟。

一、变量

  在JavaScript中就用一个变量名表示变量,变量名是大小写英文、数字、$_的组合,不能用数字开头。变量名也不能是JavaScript的关键字;

1、变量的声明

  (1)var:申明一个变量,用=对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var申明一次;

   ES6中新增let和const

  (2)let:所声明的变量,只在let命令所在的代码块内有效;必须先声明,不存在变量提升;不允许在相同作用域内,重复声明同一个变量;

  (3)const声明一个只读的常量。一旦声明,常量的值就不能改变;只声明不赋值,就会报错。

2、变量作用域

  (1)全局变量:声明在函数外部的变量;浏览器环境中全局变量为window属性;

  (2)函数作用域:针对局部变量来说的,在函数中定义的变量在函数外不能获取;

  (3)块级作用域(ES6):

二、数据类型

  6种基本类型

  • 数值(number):整数和小数(比如13.14
  • 字符串(string):文本(比如Hello World)。
  • 布尔值(boolean):表示真伪的两个特殊值,即true(真)和false(假)
  • undefined:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值
  • null:表示空值,即此处的值为空。
  • 对象(object):各种值组成的集合。

<原始类型>

1、字符串(string):

  1.1  字符串

  • 字符串是以单引号'或双引号"括起来的任意文本
  • ASCII字符可以以\x##形式的十六进制表示
  • JavaScript 允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点。(常用于字符图标)

  1.2 字符串常用方法及属性

  (1)获取字符长度:.length

  (2)获取第n位字符  .charAt(n)

  (3)获取第n位字符编码 .charCodeAt(n)

  (4)拼接字符串  a.concat('123','abc')

  (5)获取、搜索字符串位置方法 str.indexOf('a')   str.lastIndexOf('a')    找不到时返回-1

  (6)删除字符串前后的空格 str.trim()

  (7)截取字符串操作方法

    •  str.slice(start,end);           两个参数可正可负,负值代表从右截取,返回值:[start,end] 也就是说返回从start到end-1的字符
    •  str.substring(start,end);  两个参数都为正数,返回值:[start,end) 也就是说返回从start到end-1的字符
    •  str.substr(start,length);     第一个参数指定子字符串开始位置,第二个参数表示返回的字符个数

  (8)字符串大小写转换方法

    • str.toLowerCase();
    • str.toUpperCase();

  (9)字符串分割成字符串数组   str.split(separator,limit); separator指定字符串或正则,limit指定数组的最大长度 ;str.split("");  每个字符都被分割 ;数组变成字符串arr.join('-')

  (10)字符串模式匹配方法

    • str.match(rgExp);  
    • str.test(rgExp);    

  (11)替换第一个子字符串  str.replace("at","one")

  1.3字符串模板语法  

  反引号(``)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量;

  模板字符串中嵌入变量,需要将变量名写在${}之中

 2、数值(Number)

  2.1 数值 

  •  JavaScript不区分整数和浮点数,统一用Number表示 ;
  •  在JavaScript的内部采用IEEE754格式来表示数字,所以不区分整数和浮点数,都是用64位浮点数的形式储存。就是说,在JavaScript内部,就根本没有小数。但是有些运算必须得需要整数完成,所以JavaScript有时会把64位的浮点数转换成32位的整数,再进行运算。  
123; // 整数1230.456; // 浮点数0.4561.2345e3; // 科学计数法表示1.2345x1000,等同于1234.5-99; // 负数NaN; // NaN表示Not a Number,当无法计算结果时用NaN表示Infinity; // Infinity表示无限大,当数值超过了JavaScript的Number所能表示的最大值时,就表示为Infinity

  2.2 表示方法

   2.2.1 整数
  • 二进制:有前缀0b的数值,出现0,1以外的数字会报错
  • 八进制:有前缀0o的数值,或者是以0后面再跟一个数字(0-7)。如果超出了前面所述的数值范围,则会忽略第一个数字0,视为十进制数
  •     注意:八进制字面量在严格模式下是无效的,会导致支持该模式的JavaScript引擎抛出错误 
  • 十六进制:有前缀0x,后跟任何十六进制数字(0~9及A~F),字母大小写都可以,超出范围会报错
  • 十进制

  ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示

0b111110111 === 503 // true0o767 === 503 // true
  2.2.2 浮点数

  所谓浮点数,就是该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。与整数不同,浮点数只能用十进制来表示

  浮点数的精度远远不如整数,所以设计浮点数的运算好比较要小心

console.log(0.1 + 0.2 == 0.3); //false 0.30000000000000004console.log(0.6/0.2); //2.9999999999999996

 

  2.2.3科学计数法

  对于那些极大极小的数值,可以用e表示法(即科学计数法)表示的浮点数值表示。用e表示法表示的数值等于e前面的数值乘以10的指数次幂

  以下两种情况,JavaScript会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。

  • 小数点前的数字多余21位
console.log(1234567890123456789012);// 1.2345678901234568e+21 console.log(123456789012365648787); //123456789012365660000
  • 小数点后面的0多余5位
console.log(0.0000006);//6e-7console.log(0.000006); //0.000006

  2.3 范围 

  • 由于内存的限制,ECMAScript并不能保存世界上所有的数值,所以就有了最大值和最小值
  • 最小值保存在Number.MIN_VALUE中,这个值是5e-324
  • 最大值保存在Number.MAX_VALUE,这个值是1.7976931348623157e+308
console.log(Number.MIN_VALUE) //5e-324console.log(Number.MAX_VALUE); //1.7976931348623157e+308
  • 如果数字超过最大值,javascript会返回Infinity,这称为正向溢出(overflow);
  • 如果等于或超过最小负值-1023(即非常接近0),javascript会直接把这个数转为0,这称为负向溢出(underflow)
  • 如果要想确定一个数值是不是有穷的,可以使用isFinite()函数。这个函数在参数位于最小与最大数值之间时会返回true
var result = Number.MAX_VALUE + Number.MAX_VALUE;console.log(isFinite(result)); //false

  2.4 常见问题

  • typeof NaN // 'number'  ---NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于Number
  • NaN === NaN // false   ---NaN不等于任何值,包括它本身
  • (1 / +0) === (1 / -0) // false  ---除以正零得到+Infinity,除以负零得到-Infinity,这两者是不相等的

  2.4 数值常用方法

  parseInt('123',10)  用于将字符串转为整数,方法还可以接受第二个参数(2到36之间),表示被解析的值的进制

  parseFloat('314e-2') 用于将一个字符串转为浮点数。

  isNaN(123)      用于方法可以用来判断一个值是否为NaN

3、布尔值(boolean)

  (1)布尔值代表“真”和“假”两个状态。“真”用关键字true表示,“假”用关键字false表示。布尔值只有这两个值。

  (2)以下运算返回布尔值:

    •   两元逻辑运算符: && (And),|| (Or)
    •   前置逻辑运算符: ! (Not)
    •   相等运算符:===!====!=
    •   比较运算符:>>=<<=

  (3)下面六个值被转为false,其他值都视为true

    •   undefined  
    •   null  
    •   false  
    •   0  
    •   NaN  
    •   ""''(空字符串)

<合成类型>

4、对象(object)

  4.1对象基本概念

  •  合成类型(complex type)
  •  对象(object)是 JavaScript 语言的核心概念,也是最重要的数据类型;
  •  对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合;
  •  JavaScript 规定,如果行首是大括号,一律解释为语句(即代码块)。如果要解释为表达式(即对象),必须在大括号前加上圆括号。

  4.2键名

  •  对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键值),所以加不加引号都可以;
  •  如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错;
  • 对象的属性之间用逗号分隔,最后一个属性后面可以加逗号(trailing comma),也可以不加;

  4.3对象的引用

  如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。

  4.4对象的方法

  • Object.keys查看一个对象本身的所有属性
  • delete命令用于删除对象的属性,删除成功后返回true;只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除(Object.defineProperty);delete命令只能删除对象本身的属性,无法删除继承的属性
  • in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false;in运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。
  • for...in循环用来遍历一个对象的全部属性
    • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
    • 它不仅遍历对象自身的属性,还遍历继承的属性。
    • hasOwnProperty方法,在循环内部判断一下,某个属性是否为对象自身的属性。
  • with语句用于操作同一个对象的多个属性时,提供一些书写的方便
var obj = {p1: 1, p2: 2,};with (obj) {  p1 = 4;  p2 = 5;}

 

5、数组(object)

  5.1 数组的基本概念

  • 数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示;
  • 本质上,数组属于一种特殊的对象。typeof运算符会返回数组的类型是object;
  • 当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)   ;
  • 数组的空位是可以读取的,返回undefined;
  • 数组的某个位置是空位,与某个位置是undefined,是不一样的。如果是空位,使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历,空位都会被跳过

  5.2 数组的构造方法

   Array是 JavaScript 的原生对象,同时也是一个构造函数,可以用它生成新的数组。   

var arr = new Array(2);

 

  Array构造函数对于不同的参数,返回不一样的结果

// 无参数时,返回一个空数组new Array() // []// 单个正整数参数,表示返回的新数组的长度new Array(1) // [ empty ]// 非正整数的数值作为参数,会报错new Array(3.2) // RangeError: Invalid array lengthnew Array(-3) // RangeError: Invalid array length// 单个非数值(比如字符串、布尔值、对象等)作为参数,// 则该参数是返回的新数组的成员new Array('abc') // ['abc']new Array([1]) // [Array[1]]// 多参数时,所有参数都是返回的新数组的成员new Array(1, 2) // [1, 2]new Array('a', 'b', 'c') // ['a', 'b', 'c']

 

  

  5.3 数组的length属性

  (1)arr.length 数组的length属性,返回数组的成员数量  

    • JavaScript 使用一个32位整数,保存数组的元素个数。这意味着,数组成员最多只有 4294967295 个(232 - 1)个,也就是说length属性的最大值就是 4294967295
    • 如果最后一个元素后面有逗号,并不会产生空位。也就是说,有没有这个逗号,结果都是一样的
    • delete命令删除一个数组成员,会形成空位,并且不会影响length属性

  (2)length属性是可写的

    • 如果人为设置一个小于当前成员个数的值,该数组的成员会自动减少到length设置的值  ;清空数组的一个有效方法,就是将length属性设为0
    • 如果人为设置length大于当前元素个数,则数组的成员数量会增加到这个值,新增的位置都是空位 ;length属性设为大于数组个数时,读取新增的位置都会返回undefined

  (3)可使用检查某个键名是否存在的运算符in。   

  5.4 数组静态方法Array.isArray()

   Array.isArray方法返回一个布尔值,表示参数是否为数组。它可以弥补typeof运算符的不足

  5.5数组常见方法

  (1)valueOf方法是一个所有对象都拥有的方法,表示对该对象求值;alueOf方法返回数组本身;

  (2)toString方法也是对象的通用方法,数组的toString方法返回数组的字符串形式。

  (3)push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。

  (4)pop方法用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组。对空数组使用pop方法,不会报错,而是返回undefined

  (5)shift方法用于删除数组的第一个元素,并返回该元素。shift方法可以遍历并清空一个数组。

  (6)unshift方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。

  (7)join方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔。如果数组成员是undefinednull或空位,会被转成空字符串

var a = [1, 2, 3, 4];a.join(' ') // '1 2 3 4'a.join(' | ') // "1 | 2 | 3 | 4"a.join() // "1,2,3,4"

 

  (8)concat方法用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变。

['hello'].concat(['world'])// ["hello", "world"]['hello'].concat(['world'], ['!'])// ["hello", "world", "!"][].concat({a: 1}, {b: 2})// [{ a: 1 }, { b: 2 }][2].concat({a: 1})// [2, {a: 1}]

 

  (9)reverse方法用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组。

  (10)slice方法用于提取目标数组的一部分,返回一个新数组,原数组不变。arr.slice(start, end);

     slice方法的一个重要应用,是将类似数组的对象转为真正的数组。

Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })// ['a', 'b']Array.prototype.slice.call(document.querySelectorAll("div"));Array.prototype.slice.call(arguments);

 

  (11)splice方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组。

      arr.splice(start, count, addElement1, addElement2, ...);
var a = ['a', 'b', 'c', 'd', 'e', 'f'];a.splice(4, 2) // ["e", "f"]a // ["a", "b", "c", "d"]
var a = ['a', 'b', 'c', 'd', 'e', 'f'];a.splice(4, 2, 1, 2) // ["e", "f"]a // ["a", "b", "c", "d", 1, 2]
var a = [1, 1, 1];a.splice(1, 0, 2) // []a // [1, 2, 1, 1]
var a = [1, 2, 3, 4];a.splice(2) // [3, 4]a // [1, 2] 如果只提供第一个参数,等同于将原数组在指定位置拆分成两个数组

  (12)sort方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数

  [ { name: "张三", age: 30 },{ name: "李四", age: 24 },{ name: "王五", age: 28  }].sort(function (o1, o2) {
return o1.age - o2.age;})

  (13)map方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回;

  var numbers = [1, 2, 3];  numbers.map(function (n) {      return n + 1;  });// [2, 3, 4]  numbers// [1, 2, 3]
  • map方法的回调函数有三个参数,elem为当前成员的值,index为当前成员的位置,arr为原数组([1, 2, 3])。
  • map方法还可以接受第二个参数,用来绑定回调函数内部的this变量

  (14)forEach 方法与map方法很相似,也是对数组的所有成员依次执行参数函数。但是,forEach方法不返回值,只用来操作数据。这就是说,如果数组遍历的目的是为了得到返回值,那么使用map方法,否则使用forEach方法。

  •  注意,forEach方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用for循环 ;
  •  forEach方法不会跳过undefinednull,但会跳过空位。;
var out = [];[1, 2, 3].forEach(function(elem) {  this.push(elem * elem);}, out);out // [1, 4, 9]

  (15)filter方法用于过滤数组成员,满足条件的成员组成一个新数组返回。

     它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组

[1, 2, 3, 4, 5].filter(function (elem) {  return (elem > 3);})// [4, 5]

  (16)some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false

var arr = [1, 2, 3, 4, 5];arr.some(function (elem, index, arr) {  return elem >= 3;});// true

  (17)every方法是所有成员的返回值都是true,整个every方法才返回true,否则返回false

var arr = [1, 2, 3, 4, 5];arr.every(function (elem, index, arr) {  return elem >= 3;});// false

  (18)reduce方法和reduceRight方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,reduce是从左到右处理(从第一个成员到最后一个成员),reduceRight则是从右到左(从最后一个成员到第一个成员),其他完全一样。

[1, 2, 3, 4, 5].reduce(function (a, b) {  console.log(a, b);  return a + b;})// 1 2// 3 3// 6 4// 10 5//最后结果:15

  (19)indexOf方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1

      indexOf方法还可以接受第二个参数,表示搜索的开始位置。

['a', 'b', 'c'].indexOf('a', 1) // -1

  (20)lastIndexOf方法返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1

6、函数(Function)

  •  函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。
  • JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。

  6.1函数的声明

  (1)function命令声明的代码区块,就是一个函数。function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。

function print(s) {  console.log(s);}

  (2)采用变量赋值的写法

var print = function(s) {  console.log(s);};

  (3)Function构造函数

    Function构造函数接受三个参数,除了最后一个参数是add函数的“函数体”,其他参数都是add函数的参数

var add = new Function(  'x',  'y',  'return x + y');

   6.2函数的使用

  (1)调用函数时,要使用圆括号运算符。圆括号之中,可以加入函数的参数。

  (2)函数体内部的return语句,表示返回。JavaScript 引擎遇到return语句,就直接返回return后面的那个表达式的值,后面即使还有语句,也不会得到执行。也就是说,return语句所带的那个表达式,就是函数的返回值。return语句不是必需的,如果没有的话,该函数就不返回任何值,或者说返回undefined

  (3)函数可以调用自身,这就是递归(recursion)。下面就是通过递归,计算斐波那契数列的代码。

function fib(num) {  if (num === 0) return 0;  if (num === 1) return 1;  return fib(num - 2) + fib(num - 1);}fib(6) // 8

 

  6.3函数与数据类型

  • JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。
  • 由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。
function add(x, y) {  return x + y;}// 将函数赋值给一个变量var operator = add;// 将函数作为参数和返回值function a(op){  return op;}a(add)(1, 1) // 函数
// 2

 

  6.4函数名的提升

  JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错

f();function f() {}

  表面上,上面代码好像在声明之前就调用了函数f。但是实际上,由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用赋值语句定义函数,JavaScript 就会报错。

f();var f = function (){};// TypeError: undefined is not a function

  6.5不得在非函数的代码块中声明函数,最常见的情况就是iftry语句

if (foo) {  function x() {}}try {  function x() {}} catch(e) {  console.log(e);}

 

  6.6函数的属性和方法

  (1)f.name 函数的name属性返回函数的名字

  (2)f.length属性返回函数预期传入的参数个数,即函数定义之中的参数个数

  (3)f.toString方法返回一个字符串,内容是函数的源码

  6.7函数的作用域

  (1)作用域(scope)指的是变量存在的范围。在 ES5 的规范中,Javascript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。

  •   函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。
  •  在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)
  • 函数内部定义的变量,会在该作用域内覆盖同名全局变量。

  (2)与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部

function foo(x) {  if (x > 100) {    var tmp = x - 100;  }}// 等同于function foo(x) {  var tmp;  if (x > 100) {    tmp = x - 100;  };}

 

  (3)函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

var a = 1;var x = function () {  console.log(a);};function f() {  var a = 2;  x();}f() // 1

  函数x是在函数f的外部声明的,所以它的作用域绑定外层,内部变量a不会到函数f体内取值,所以输出1,而不是2

  函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。

  同样的,函数体内部声明的函数,作用域绑定函数体内部

function foo() {  var x = 1;  function bar() {    console.log(x);  }  return bar;}var x = 2;var f = foo();f() // 1

 

  6.8参数

  函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数。

  (1)函数参数不是必需的,Javascript 允许省略参数。

  (2)如果有同名的参数,则取最后出现的那个值

  (3)arguments 对象

  •  由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来

  • arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
var f = function (one) {  console.log(arguments[0]);  console.log(arguments[1]);  console.log(arguments[2]);}f(1, 2, 3)// 1// 2// 3

 

  正常模式下,arguments对象可以在运行时修改。

  严格模式下,arguments对象是一个只读对象,修改它是无效的,但不会报错。

  通过arguments对象的length属性,可以判断函数调用时到底带几个参数。

  虽然arguments很像数组,但它是一个对象。数组专有的方法(比如sliceforEach),不能在arguments对象上直接使用

  如果要让arguments对象使用数组方法,真正的解决方法是将arguments转为真正的数组。下面是两种常用的转换方法:slice方法和逐一填入新数组。

var args = Array.prototype.slice.call(arguments);// 或者var args = [];for (var i = 0; i < arguments.length; i++) {  args.push(arguments[i]);}

 

  arguments对象带有一个callee属性,返回它所对应的原函数。

var f = function () {  console.log(arguments.callee === f);}f() // true

  6.9闭包

  变量作用域。前面提到,JavaScript 有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。

var n = 999;function f1() {  console.log(n);}f1() // 999

  上面代码中,函数f1可以读取全局变量n。但是,函数外部无法读取函数内部声明的变量。

function f1() {  var n = 999;}console.log(n)

  上面代码中,函数f1内部声明的变量n,函数外是无法读取的

  如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。

function f1() {  var n = 999;  function f2() {  console.log(n); // 999  }}

上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是 JavaScript 语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗  

function f1() {  var n = 999;  function f2() {    console.log(n);  }  return f2;}var result = f1();result(); // 999

上面代码中,函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

function createIncrementor(start) {  return function () {    return start++;  };}var inc = createIncrementor(5);inc() // 5inc() // 6inc() // 7

上面代码中,start是函数createIncrementor的内部变量。通过闭包,start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包inc使得函数createIncrementor的内部环境,一直存在。所以,闭包可以看作是函数内部作用域的一个接口。为什么会这样呢?原因就在于inc始终在内存中,而inc的存在依赖于createIncrementor,因此也始终在内存中,不会在调用结束后,被垃圾回收机制回收。

闭包的另一个用处,是封装对象的私有属性和私有方法

function Person(name) {  var _age;  function setAge(n) {    _age = n;  }  function getAge() {    return _age;  }  return {    name: name,    getAge: getAge,    setAge: setAge  };}var p1 = Person('张三');p1.setAge(25);p1.getAge() // 25

上面代码中,函数Person的内部变量_age,通过闭包getAgesetAge,变成了返回对象p1的私有变量。

注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

  6.10立即调用的函数表达式

  在 Javascript 中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。

(function(){ /* code */ }());// 或者(function(){ /* code */ })();

 

  上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称 IIFE。

  注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个 IIFE,可能就会报错。

!function () { /* code */ }();~function () { /* code */ }();-function () { /* code */ }();+function () { /* code */ }();

   通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

// 写法一var tmp = newData;processData(tmp);storeData(tmp);// 写法二(function () {  var tmp = newData;  processData(tmp);  storeData(tmp);}());

 

7、null 和 undefined

 7.1 undefined是一个表示”此处无定义”的原始值,转为数值时为NaN

 7.2  null是一个表示“空”的对象,转为数值时为0

if (!undefined) {  console.log('undefined is false');}// undefined is falseif (!null) {  console.log('null is false');}// null is falseundefined == null// trueNumber(null) // 05 + null // 5Number(undefined) // NaN5 + undefined // NaN

 

 

三、数据类型判断

1、typeof

typeof运算符可以返回一个值的数据类型

  • 数值、字符串、布尔值分别返回numberstringboolean
  • 函数返回function
  • undefined返回undefined
  • 对象返回object
typeof 123 // "number"typeof '123' // "string"typeof false // "boolean"function f() {}typeof f  // "function"typeof undefined // "undefined"typeof window // "object"typeof {} // "object"typeof [] // "object"

2、instanceof

instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例

(1)instanceof运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象(prototype),是否在左边对象的原型链上。

var v = new Vehicle();v instanceof Vehicle // true

(2)由于instanceof检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回true

var d = new Date();d instanceof Date // trued instanceof Object // true

(3)instanceof的原理是检查右边构造函数的prototype属性,是否在左边对象的原型链上。有一种特殊情况,就是左边对象的原型链上,只有null对象。这时,instanceof判断会失真。

var obj = Object.create(null);typeof obj // "object"Object.create(null) instanceof Object // false

(4)区别array和obj

var x = [1, 2, 3];var y = {};x instanceof Array // truey instanceof Object // true

 (5)instanceof运算符只能用于对象,不适用原始类型的值。

var s = 'hello';s instanceof String // false

 

 (6)对于undefinednullinstanceOf运算符总是返回false

undefined instanceof Object // falsenull instanceof Object // false

 

 3、Object.prototype.toString

(1)基本类型判断

console.log(Object.prototype.toString.call("jerry"));//[object String]console.log(Object.prototype.toString.call(12));//[object Number]console.log(Object.prototype.toString.call(true));//[object Boolean]console.log(Object.prototype.toString.call(undefined));//[object Undefined]console.log(Object.prototype.toString.call(null));//[object Null]

 

(2)判断原生引用类型

console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]console.log(Object.prototype.toString.call(function(){}));//[object Function]console.log(Object.prototype.toString.call([]));//[object Array]console.log(Object.prototype.toString.call(new Date));//[object Date]console.log(Object.prototype.toString.call(/\d/));//[object RegExp]

 

(3)自定义类型

这种方法不能准确判断personPerson类的实例,而只能用instanceof 操作符来进行判断

function Person(name, age) {    this.name = name;    this.age = age;}var person = new Person("Rose", 18);Object.prototype.toString.call(arr); // "[object Object]"

 

 

 

 

注:

部分内容引用和参考:

 

转载于:https://www.cnblogs.com/pangys/p/5592297.html

你可能感兴趣的文章
Android 自定义View (三) 圆环交替 等待效果
查看>>
设置虚拟机虚拟机中fedora上网配置-bridge连接方式(图解)
查看>>
HEVC播放器出炉,迅雷看看支持H.265
查看>>
[置顶] Android仿人人客户端(v5.7.1)——人人授权访问界面
查看>>
Eclipse 调试的时候Tomcat报错启动不了
查看>>
【安卓5】高级控件——拖动条SeekBar
查看>>
ES6内置方法find 和 filter的区别在哪
查看>>
Android入门之文件系统操作(二)文件操作相关指令
查看>>
Android实现 ScrollView + ListView无滚动条滚动
查看>>
Swift 中的指针使用
查看>>
Swift - 使用闭包筛选过滤数据元素
查看>>
alue of type java.lang.String cannot be converted to JSONObject
查看>>
搜索引擎选择: Elasticsearch与Solr
查看>>
JAVA设计模式之简单工厂模式与工厂方法模式
查看>>
③面向对象程序设计——封装
查看>>
【19】AngularJS 应用
查看>>
Spring
查看>>
Linux 系统的/var目录
查看>>
Redis学习---Redis操作之其他操作
查看>>
WebService中的DataSet序列化使用
查看>>