thirteen
一:链式调用
1.1 尝试打印对象中的所有方法
需求:现在有一个对象
sched,需要打印这个对象内的所有方法,只能在一行中把所有的方法全部调用出来,不能一行一行的使用对象名加方法名来调用。
代码范例1:错误的使用方法
var sched = {
wakeup: function(){console.log('Running');},
morning: function(){console.log('Going shopping');},
noon: function(){console.log('Having a rest');},
afternoon: function(){console.log('Studying');},
evening: function(){console.log('Walking');},
night: function(){console.log('Sleeping');}
}
//尝试按这种方式来调用
sched.wakeup().morning().noon().afternoon().evening().night();
//Print Result:只输出 Running ,然后报错,显然这个方式是不可行的。
1.2 尝试把所有方法汇总到一个方法内
这种方法是可行的,但不是我们需要,继续往下看。
代码范例2:验证上述内容
var sched = {
wakeup: function () {
console.log('Running');
},
morning: function () {
console.log('Going shopping');
},
noon: function () {
console.log('Having a rest');
},
afternoon: function () {
console.log('Studying');
},
evening: function () {
console.log('Walking');
},
night: function () {
console.log('Sleeping');
},
all: function () {
this.wakeup();
this.morning();
this.noon();
this.afternoon();
this.evening();
this.night();
}
}
sched.all();
1.3 尝试使用 return this 方式
在对象方法中,使用
return指令返回一个this,按照以往学习对象的知识中,可以很快的就想到,this就是指向对象本身,所以在第一次使用方法名后,就立马返回了一个对象名,然后在用点语法在加上方法名,又可以成功的执行下一个方法,如此往复,直到最后一个方法,就不需要
return this。
代码范例3:验证上述内容
var sched = {
wakeup: function (){
console.log('Running');
//这里的 return,可以理解成,执行 sched 对象中的 wakeup方法后,在返回对象名
return this;
},
morning: function (){
console.log('Going shopping');
return this;
},
noon: function (){
console.log('Having a rest');
return this;
},
afternoon: function (){
console.log('Studying');
return this;
},
evening: function (){
console.log('Walking');
return this;
},
night: function (){
console.log('Sleeping');
}
}
sched.wakeup().morning().noon().afternoon().evening().night();
问:测试是否真的返回对象名?
答:观察 代码范例5 中的结果,可以得知确实没有问题。
代码范例5:验证上述内容
var sched = {
wakeup: function (){
console.log('Running');
return this;
}
}
console.log(sched.wakeup() === sched);
//Print Result: Running, true
二:对象遍历
2.1 拼接对象属性名
通过 代码范例6 ,可以很清楚的知道:
对象的属性名,在拼接时,一定要用引号包裹起来,由此可以认为,属性名就是字符串;
在拼接时,需要使用
[]来包裹属性名;同时,属性名的拼接,可以是表达式,也可以是变量;
Tips1:
在早期的 JavaScript 引擎( ECMAScript 1 之前)中,是没有
.点语法来访问对象中的属性名.当时,访问对象的唯一方式是使用方括号[]的形式。后来,点语法被引入( ECMAScript 1),并逐渐成为访问对象属性的首选方式,然而,为了兼容早期的 JavaScript 代码,现代 JavaScript 引擎仍然会把点语法
转换成方括号的形式来访问。
Tips2:
方括号
[]与点语法.的使用场景:
- 方括号在处理动态属性名或属性名为变量时特别有用;
- 点语法则在访问静态、已知属性时更为简洁明了。
代码范例6:验证上述内容
//演示代码
var myLang = {
No1: 'HTML',
No2: 'CSS',
No3: 'JavaScript',
myStudyingLang: function(num){
//常用,也是推荐的方法
//console.log(this[`No` + num]);
//根据上面常用的方法,衍生出来的 ---> 表达式
//console.log(this[(`No` + num)]);
//同上 ---> 变量
// var putTogether = 'No' + num; //隐式转换 num 为字符串
// console.log(this[putTogether]);
}
}
myLang.myStudyingLang(1);
//Print Result:HTML
//使用 [] 方括号一样可以访问对象中的属性
var obj = {
name : 123
}
console.log(obj['name']);
//Print Result:123
2.2 对象枚举
什么是枚举?
在 JavaScript 中,是没有所谓正真意义上的枚举,这个所谓指的是:在一些强类型的编程语言(C、C++、Java)中,枚举是一种内置的数据类型,它包含
了一个固定的、有限的值集合,这些值具有共同的特性。但在 JavaScript 中,没有内置的枚举类型,通常是通过对象或其他结构来模拟枚举的行为。
在 JavaScript 中可以理解成,一组有共同特性的数据的集合。
什么是遍历?在一组信息内,按顺序一个个获取其信息的过程。
在 JavaScript 中,提到枚举,就会提到遍历,可以理解为,有遍历就有枚举,有枚举也就有遍历,它们两个之间属于相辅相成关系。
在JavaScript中,枚举(遍历)的方法
for(var key in object)
- key:用于存储当前遍历到的属性名。可以自由命名这个变量,但需要注意避免与上下文中的其他变量名发生冲突,以免造成全局变量名污染。
- object:需要枚举(遍历)的数据名。
思考 car.key 为什么会是 undefined?
- JavaScript 引擎会先将
car.key转换为car['key'];- 然后将
key认为是对象中的一个属性名,进行查找;- 查找不到,自然就会返回 undefined;
- 其过程为 car.key ---> car['key'] ---> undefined
代码范例7:验证上述内容
//在 JavaScript 中,数组是一种特殊的对象
//循环数组的过程,也是遍历过程,也就是枚举数组过程
var arr = [1, 2, 3, 4, 5]
//第一种枚举数组方法
for(var i = 0; i < arr.length; i++){
console.log(arr[i]);
}
//第二种枚举数组方法,但不推荐,一般是 for循环以及 forEach,后面学到在说。
for(var key in arr){
console.log(arr[key]);
}
//Print Result: 1, 2, 3, 4, 5
---------------------------------------------
//对象枚举
var car = {
brand: 'Benz',
color: 'red',
displacement: '3.0T',
length: '3',
width: '2'
}
//枚举出该对象所有的属性名
for(var key in car){
console.log(key);
}
//枚举出该对象所有的属性值
for(var key in car){
console.log(car[key]);
}
//思考 car.key 为什么会是 undefined?
//思考 car['key'] 为什么会是 undefined?
for(var key in car){
console.log(car.key); //car.key ---> car['key'] --->undefined
console.log(car['key']); //同上原理
}
2.3 hasOwnProperty 方法的作用
通过观察代码范例8时,发现使用
for(var key in object)的方式枚举,会把对象自身的原型也一起打印出来。 在 JavaScript 中,
hasOwnProperty方法是用来检查一个对象是否含有特定的自身属性,而不是继承自其原型链的属性。这个方法对于区分对象自有属性和从原型链继承的属性特别有用,尤其是在使用
for...in循环遍历对象属性时
object.hasOwnProperty(key)方法解释:
object ---> 对象名称;
key ---> 对象内的属性名或方法名;
hasOwnProperty 方法返回一个布尔值,true 或 false;
如果值为 true,key 为对象自有属性名或方法名而不是通过原型继承下来的;
如果值为 false,key 为属性名或方法名是通过原型继承下来的;
代码范例8:验证上述内容
function Car(){
this.brand = 'Benz';
this.color = 'red';
}
Car.prototype.displacement = '3.0T';
var car = new Car();
//枚举对象
for(var key in car){
console.log(key + ':' + car[key]);
}
//Print Result:brand:Benz, color:red, displacement:3.0T
//只打印对象自身的属性或方法
function Car(){
this.brand = 'Benz';
this.color = 'red';
}
Car.prototype.displacement = '3.0T';
var car = new Car();
//枚举对象
for(var key in car){
//下面的条件判断语句,可以理解成:
//1、返回值为true,则代表是当前对象的自有属性名,即进入相应语句内;
//2、返回值为false,则代表不是当前对象的自有属性名而是从原型上继承的,即什么也不做。
if(car.hasOwnProperty(key)){
console.log(key + ':' + car[key]);
}
}
2.4 in 的用法
通过观察代码范例9时,
in的作用就是为了判断对象中是否包含了相应的属性名或方法名,并且是可以直接向上查找,也就是在对象自身没有找到,就会去原型上逐个寻找,如果有则返回 true,否则返回 false。
代码范例9:验证上述内容
function Car(){
this.brand = 'Benz';
this.color = 'red';
}
Car.prototype.displacement = '3.0T';
var car = new Car();
console.log('displacement' in car); //true
三:零碎知识
3.1 instanceof 运算符
MDN:
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上,如果是则为true,反之false。个人理解:
可以理解为,instanceof 检测实例化对象的原型是否与构造函数中的原型链是否重合,如果是则为true,反之false。
代码范例9:验证上述内容
function Person(){}
var person = new Person();
function Car(){
this.brand = 'Benz';
this.color = 'black';
}
var car = new Car();
//按照MDN的解释,可以理解为,instanceof 检测实例化对象的原型是否与构造函数中的原型链是否重合,如果是则为true,反之false。
console.log(car instanceof Car);//true
console.log(person instanceof Car);//false
console.log(person instanceof Object);//true
//常识
console.log([] instanceof Array); //true
console.log([] instanceof Objcet); //true
console.log({} instanceof Object); //true
3.2 数据类型的判断
问:在 JavaScript 中数据类型分为两种,原始类型与引用类型,那么要如何准确直观的判断数据类型?
答:具体方法展开如下:
1、使用
typeof运算符:
- 对于原始类型的判断显示非常直观,除了
null它会返回object;- 对于引用类型,它基本都返回
object,除了函数返回function。2、使用
Object.prototype.toString.call():
- 这是一种更可靠的类型检查方法,它可以区分不同类型的对象,如数组、、对象、日期等。
3、数组特定的检查:
- 对于数组,还可以使用
Array.isArray()。
代码范例10:验证上述内容
//1、typeof 运算符
console.log(typeof {}); //object
console.log(typeof []); //objcet
console.log(typeof null); //object
console.log(typeof function(){}); //function
//2、Object.prototype.toString.call()
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
var regex = /abc/;
console.log(Object.prototype.toString.call(regex));//[object RegExp]
//3、Array.isArray()
console.log(Array.isArray([])); //true
3.3 Object.prototype.toString.call() 的认识
问:为什么
Object.prototype.toString.call()方法能返回数据类型?答:
Object.prototype.toString()返回为[object Type],这里的Type是对象类型的字符串,它们包括:
- [
Array]、[Function]、[Error]、[Boolean]、[Number]、[String]、[Date]、[RegExp];- 在调用
Object.prototype.toString.call()方法,被call重新指向当前传入的参数,此时参数的数据类型就会与Type字符串相映射,且返回。
代码范例11:验证上述内容
var a = [];
var b = {};
var c = 'abc';
var d = 123456;
var e = true;
//以下,不管传入什么类型的数据,一律返回的是 [object Object]
console.log(Object.prototype.toString(a)); //[object Object]
console.log(Object.prototype.toString(c)); //[object Object]
console.log(Object.prototype.toString(e)); //[object Object]
//在使用 call 的时候,指向了参数的本身,情况就不一样了
console.log(Object.prototype.toString.call(a)); //[object Array]
console.log(Object.prototype.toString.call(c)); //[object String]
console.log(Object.prototype.toString.call(e)); //[object Boolean]
//常识
//原始类型中的 null 和 undefined ,也可以这个方法来检测
console.log(Object.prototype.toString.call(null));
console.log(Object.prototype.toString.call(undefined));
3.4 this的阶段性认识
1、普通函数的
this指向:
- 在普通函数内部使用
this,只要没有实例这个函数时,this的指向一定是window,那么也就可以在外部直接访问;- 例如
this.d = 3--->window.d = 3--->d = 3,具体看范例。2、
call/apply改变this指向;3、构造函数的
this指向实例化后的对象;
代码范例12:验证上述内容
//普通函数
function test(){
this.d = 3;
}
test(); //函数运行
console.log(this.d); //3
console.log(window.d); //3
console.log(d); //3
3.5 callee/caller
callee/caller不常用,在 ES5 中的严格模式下会抛出 TypeError 错误,在 ES6 中已经弃用,同时arguments也是一样,以上知识,需要知道。
callee:是arguments对象的一个属性。用于获取当前函数的函数体。这在匿名函数时很有用,注意是整个函数体而不是函数名。
caller:当 a 函数被 b 函数调用时,且在 a 函数内写入a.caller语句,然后 b 函数执行时,则返回 b 函数的函数体。作用就是返回调用该函数的函数。
代码范例13:验证上述内容
//callee
function test(){
console.log(arguments.callee);
}
test();
//Print Result: ƒ test(){console.log(arguments.callee);}
//callee 严格模式下报错
"use strict";
function test(){
console.log(arguments.callee);
}
test();//Print Result:Uncaught TypeError:......
//callee的应用
//递归累加
function sum(n){
if(n <= 1){
return 1;
}
return n + sum(n - 1);
}
sum(10); //Print Result:55
//使用立即执行函数递归累加并赋值给一个变量
var sum = (function(n){
if(n <= 1){
return 1;
}
return n + arguments.callee(n - 1);
})(10);
console.log(sum); //Print Result:55
//caller
function test1(){
test2();
}
function test2(){
console.log(test2.caller);
}
test1();//Print Result: ƒ test1(){test2();}