javascript面向对象编程 知识回顾

本文详细介绍了JavaScript的基本数据类型、数组、变量的概念及其作用域,深入探讨了预定义函数的使用方法,同时讲解了函数作为数据的特点,以及闭包的重要概念和实际应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 基本数据类型、数组

1.1 变量

变量名可以由字母、数字、下划线及美元符号组合而成。但不能以数字开头

变量名区分大小写。

变量声明未初始化,默认初始化为undefined。

1.2 基本数据类型

  1. 数字 —— 包括浮点数与整数
  2. 字符串 —— 包括由任意数量字符组成的序列
  3. 布尔值 —— 包括true和false
  4. undefined —— 当我们试图访问一个不存在的变量时,就会得到一个特殊值:undefined。除此之外,使用已声明却未赋值的变量也会如此。因为JavaScript会自动将变量在初始化之前的值设定为undefined。而undefined类型的值只有一个——undefined。
  5. null —— 这是另一种只包含一个值的特殊数据类型。所谓的null值,通常是指没有值或空值,不代表任何东西。
    null与undefined最大的不同在于,被赋予null的变量通常被认为是已经定义了的,只不过它不代表任何东西。

任何不属于上述五种基本类型的值都会被认为是一个对象。甚至有时候我们也会将null视为对象——一个不代表任何东西的对象(东西)。

JavaScript中的数据类型主要分为以下两个部分:

  • 基本类型(上述列出的五种类型)
  • 非基本类型(即对象)

1.3 查看类型操作符——typeof

该操作符返回一个代表数据类型的字符串,返回结果可能如下:

  • “number”
  • “string”
  • “boolean”
  • “undefined”
  • “object”
  • “function”

当一个数字以0开头时,表示一个八进制数
当一个数字以0x开关时,表示一个十六进制值
当一个数字可以表示成1e1(或者1e+1、1E1、1E+1)这样的指数形式,意思是在数字1后面加1个0,也就是10。

1.3.1 Infinity

特殊值,代表超出了JavaScript处理范围的数值。但Infinity依然是一个数字。

javaScript所能处理的最大值是1.7976931348623157e+308,而最小值为5e-324。

任何数除以0结果也为Infinity。

正负Infinity相加,不会得到0,而是会得到一个叫做NaN(Not A Number的缩写,即不是数字)的东西。

Infinity与其他任何操作数执行任何算术运算的结果也都等于Infinity。

1.3.2 NaN

不是数字,但属于数字类型,为特殊的数字。

算术运算中使用了不恰当的操作数,导致运算失败,该运算就会返回NaN。

var a = 10 * “f”;
a;
NaN

而且NaN是有传染性的,只要我们的算术运算中存在一个NaN,整个运算就会失败。

1 + 2 + NaN;
NaN

1.3.3 字符串

当一个数字字符串用于算术运算中的操作数时,该字符串会在运算中被当做数字类型来使用。***(由于加法操作符的歧义性,这条规则不适用于加法运算。)***

字符串转换操作失败则返回一个NaN值。

1.3.4 逻辑运算符

JavaScript中有三种逻辑运算符,它们都属于布尔运算。

  • ! —— 逻辑非(取反)
  • && —— 逻辑与
  • || —— 逻辑或

除了下面所列出特定值以外(它们将被转换为false),其余大部分值在转换为布尔值时都为true。

  • 空字符串""
  • null
  • undefined
  • 数字0
  • 数字NaN
  • 布尔值false

这6个值有时也会被称为falsy值,而其他值则被称为truthy值(包括字符串"0"、" "、"false"等)。

1.3.5 操作符优先级

逻辑运算符优先级:! > && > ||

1.3.6 特殊的

  • NaN不等于任何东西,包括它自己。

NaN == NaN
false

  • 使用一个不存在的变量,控制台返回错误信息

foo;
ReferenceError: foo is not defined

  • 对不存在的变量使用typeof操作符,不报错,返回字符串"undefined"

typeof foo;
undefined

  • 对已声明但未赋值的变量使用typeof操作符,返回字符串"undefined"

var somevar;
somevar;
typeof somevar;
undefined

  • null不能由JavaScript自动赋值

var somevar = null;
null

somevar;
null

typeof somevar;
object

  • undefined和null区别

var i = 1 + undefined;
i;
NaN

var i = 1 + null;
i;
1

转换成数字

1 * undefined;
NaN

1 * null;
0

转换成布尔值

!! undefined;
false

!! null;
false

转换成字符串

"value: " + undefined;
value: undefined

"value: " + null;
value: null

二 函数

函数通常都会有返回值,如果某个函数没有显式的返回值,我们就会默认它的返回值为undefined。

2.1 预定义函数

2.1.1 parseInt()

将收到的任何值(通常是字符串)转换成整数类型输出。转换失败返回NaN。

函数还有一个可选的第二参数:基数(radix),它负责设定函数所期望的数字类型——十进制、十六进制、二进制等。

未指定第二参数时默认为十进制,除以下两个情况:

  • 首参以0x开头,二参默认指定为16
  • 首参以0开头,二参默认指定为8

明确指定radix值问题比较安全的,若未指定,偶尔可能会存在错误情况,比如读取日期08这样的数据。

遇到第一个异常字符时放弃。

parseInt(‘123abc’);
123

2.1.2 parseFloat()

parseFloat()的功能与parseInt()基本相同,不过仅支持输入值转换为十进制数函数只有一个参数

与parseInt()不同的,parseFloat()还可以接受指数形式的数据。

parseFloat(‘1e10’);
10000000000


parseInt(‘1e10’);
1

2.1.3 isNaN()

确定某个输入值是否是一个可以参与算术运算的数字。可以用来检测parseInt()和parseFloat()的调用成功与否。

isNaN(NaN);
true

isNaN(123);
false

isNaN(parseInt(‘abc123’));
true

函数会试图将所接收的输入转换为数字

isNaN(‘1.23’);
false

2.1.3 isFinite()

用于检查输入是否是一个既非Infinity也非NaN的数字。

isFinite(Infinity);
false

isFinite(1e308);
true

isFinite(1e309);
false

2.1.4 URI的编码与反编码

转义URL或URI中特殊含义的字符:

编码:

encodeURI()
返回一个可用的URL

encodeURIComponent()
认为传递的仅仅是URL的一部分

反编码:

decodeURI()

decodeURLComponent()

2.1.5 eval()

将输入的字符串当做JavaScript代码来执行。

eval(‘var ii = 2;’)
ii;
2

应尽量避免使用eval():

  • 安全性方面:不确定性太大
  • 性能方面:它是一种由函数执行的“动态代码”,所以要比直接执行脚本要慢。

2.1.6 alert()

非JavaScript核心的一部分,由浏览器提供。利于调试,但这样会阻塞当前的浏览器线程,对于ajax应用程序不友好。

2.2 变量的作用域

var a = 123;
function f() {
	alert(a);  // undefined
	var a = 1;
	alert(a);  // 1
}
f();

重点:函数域始终优先于全局域。所以局部变量a会覆盖掉所有与它同名的全局变量,尽管在alert()第一次被调用时,a还没有被正式定义(即该值为undefined),但该变量本身已经存在于本地空间了。这种特殊的现象叫做提升(hoisting)。即当JavaScript执行过程进入新的函数时,这个函数内被声明的所有变量都会被移动(或者说提升)到函数最开始的地方。且被提升的只有变量的声明。只有函数体内声明的这些变量在该函数执行开始时就存在,而与之相关的赋值操作并不会被提升,

var a = 123;
function f() {
	var a;  // same as: var a = undefined;
	alert(a);  // undefined
	a = 1;
	alert(a);  // 1
}
f();

2.3 函数也是数据

函数也是赋值给变量的一种数据,所以函数的命名规则与一般变量相同——函数名不能以数字开头,并且可以由任意的字母、数字、下划线和美元符号组合而成。

2.3.1 匿名函数

var f = function(a) {
	return a;
} 

如通过上面这种方式定义的函数常被称为匿名函数(即没有名字的函数),特别是当它不被赋值给变量单独使用的时候。在这种情况下,此函数有两种优雅的用法:

  • 将匿名函数作为参数传递给其他函数,接收函数就能利用我们所传递的函数来完成某些事情。

  • 定义某个函数来执行某些一次性任务。

2.3.2 回调函数

既然函数与任何可以被赋值给变量的数据是相同的,那么它当然可以像其他数据那样被定义、删除、拷贝,以及当成参数传递给其他函数。

使用回调函数的优势:

  • 它可以让我们在不做命名的情况下传递函数(节省变量名的使用)

  • 我们可以将一个函数调用操作委托给另一函数(节省代码编写工作)

  • 它们也有助于提升性能

未使用匿名函数:

function multiplyByTwo (a, b, c, callback) {
  var i, ar = [];
  for (i = 0; i < 3; i++) {
    ar[i] = callback(arguments[i] * 2);
  }
  return ar;
}

function addOne (a) {
  return a + 1;
}

var myarr = [];
myarr = multiplyByTwo(1, 2, 3, addOne);

使用匿名函数:

function multiplyByTwo (a, b, c, callback) {
  var i, ar = [];
  for (i = 0; i < 3; i++) {
    ar[i] = callback(arguments[i] * 2);
  }
  return ar;
}

var myarr = [];
myarr = multiplyByTwo(1, 2, 3, function (a) {
  return a + 1;
});

从上面可以看出,使用匿名函数代替addOne(),这样做可以节省一个额外的全局变量,且更易于随时根据需求调整代码。

2.3.3 即时函数

匿名函数的另一种应用示例——这种函数可以在定义后立即调用。比如:

(
	function() {
		alert('boo');
	}
)();

闭合方式有以下两种:

  • 1
(function() {
	// ...
}());
  • 2
(function() {
	// ...
})();

使用即时(自调)匿名函数的好处是不会产生任何全局变量。当然,缺点在于这样的函数无法重复执行的(除非将它放在某个循环或其他函数中)。这使得即时函数非常适合于执行一些一次性的或初始化的任务。即时函数也可以有返回值。

2.3.4 内部(私有)函数

在一个函数内部定义另一个函数。

好处:

  • 有助于我们确保全局名字空间的纯净性(命名冲突机会很小)。

  • 确保私有性——这使得我们可以选择只将一些必要的函数暴露给“外部世界”,而保留属于自己的函数,使它们不为该应用程序 的其他部分所用。

2.3.5 返回函数的函数

function a() {
	alert('A!');
	return function() {
		alert('B!');
	}
}

// 两种调用返回的函数方法
// 1
> var newFunc = a();
> newFunc();

// 2
> a()();

2.3.6 能重写自己的函数

由于一个函数可以返回另一个函数,因此我们可以用新的函数来覆盖旧的。

a = a();  // 执行 alert('A!');
a();  // 执行alert('B!');

这对于要执行某些一次性初始化工作的函数来说会非常有用。这样一来,该函数可以在第一次被调用后重写自己,从而避免了每次调用时重复一些不必要的操作。

可以从外面来重定义该函数,也可以从内部重写。

function a() {
	alert('A!');
	a = function() {
		alert('B!');
	};
}

2.4 闭包

2.4.1 作用域链

在某函数内定义的所有变量在该函数外是不可见的,但如果该变量是在某代码块中被定义的(如在某个if或for语句中),那它在代码块外是可见的。

var a = 1;
function f() {
	var b = 1;
	return a;
}
f();	// 1
b;	// ReferenceError: b is not defined

如果在函数outer()中定义了另一个函数inner(),那么,在inner()中可以访问的变量既来自它自身的作用域,也可以来自其“父级”作用域。这就形成了一条作用域链接(scope chain),该链的长度(或深度)则取决于我们的需要。

2.4.2 利用闭包突破作用域链

  • 闭包#1
var a = "global variable";
var F = function () {
	var b = "local variable";
	var N = function () {
		var c = "inner local";
		return b;
	};
	return N;
};
> b;
ReferenceError: b is not defined

> var inner = F();
> inner();
"local variable"
  • 闭包#2

    将F()中定义的一个新函数N()赋值给全局变量inner。由于N()是在F()内部定义的,它可以访问F()的作用域,所以即使该函数后来升级成了全局函数,但它依然可以保留对F()作用域的访问权。

var inner; // placeholder
var F = function () {
	var b = "local variable";
	var N = function () {
		return b;
	};
	return N;
};
> F();
undefined

> inner();
"local variable"
  • 使用函数参数做闭包。

    该参数与函数的局部变量没什么不同,但它们是隐式创建的(即不需要使用var声明)。即创建一个函数,该函数将返回一个子函数,而这个子函数返回的则是其父函数的参数。

function F(param) {
	var N = function() {
		return param;
	};
	param++;
	return N;
}

> var inner = F(123);
> inner();
124

函数所绑定的是作用域本身,而不是在函数定义时该作用域中的变量或变量当前所返回的值。

  • 循环中的闭包

闭包典型错误:

function F() {
	var arr = [], i;
	for (i = 0; i < 3; i++) {
		arr[i] = function () {
			return i;
		};
	}
	return arr;
}

> var arr = F();
> arr[0]();  // 3
> arr[1]();  // 3
> arr[2]();  // 3

要知道这里为什么结果都是3

这不是我们要的结果,此处创建了三个闭包,而它们都指向了一个共同的局部变量i。但是,闭包并不会记录它们的值,它们所拥有的只是相关域在创建时的一个连接(即引用)。

纠正错误:

  • 方法1:不再直接创建一个返回i的函数了,而是将i传递给了另一个即时函数。在该函数中,i就被赋值给了局部变量x,这样一来,每次迭代中的x就会拥有各自不同的值。
function F() {
	var arr = [], i;
	for (i = 0; i < 3; i++) {
		arr[i] = (function (x) {
			return function() {
				return x;
			};
		}(i));
	}
	return arr;
}

> var arr = F();
> arr[0]();  // 0
> arr[1]();  // 1
> arr[2]();  // 2
  • 方法2:定义一个内部函数(不使用即时函数),在每次迭代操作中,在中间函数内将i的值“本地化”。
function F() {
	function binder(x) {
		return function() {
			return x;
		};
	}
	var arr = [], i;
	for (i = 0; i < 3; i++) {
		arr[i] = binder(i);
	}
	return arr;
}

> var arr = F();
> arr[0]();  // 0
> arr[1]();  // 1
> arr[2]();  // 2

2.4.3 关于闭包的应用示例:

2.4.3.1 getter与setter

假设一个变量表示某类特定值或某特定区间内的值。不想该变量暴露给外部,需要保护在相关函数内部。

所有一切通过一个即时函数来实现,我们在其中定义了全局函数,setValue()和getValue(),并以此来确保局部变量secret的不可直接访问性。

var getValue, setValue;
(function() {
	var secret = 0;
	getValue = function() {
		return secret;
	};
	setValue = function(v) {
		if (typeof v === "number") {
			secret = v;
		}
	};
}());

> getValue();  // 0
> setValue(123);
> getValue();  // 123
> setValue(false);
> getValue();  // 123

2.4.3.2 迭代器

闭包实现迭代器。将“谁是下一个”的复杂逻辑封装成易于使用的next()函数,然后只需调用它就能实现相关的遍历操作了。

// 接受数组输入的初始化函数,私有指针i始终指向数组中的下一个元素
function setup(x) {
	var i = 0;
	return function() {
		return x[i++];
	};
}

> var next = setup(['a', 'b', 'c']);
> next();  // "a"
> next();  // "b"
> next();  // "c"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值