一、对象的链式调用
对象的链式调用,需要在方法末尾返回this;return this
// 对象的链式调用,需要在方法末尾返回this;return thisvar sched = {wakeup: function(){console.log('Running');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');return this;}}sched.wakeup().morning().noon().afternoon().evening().night();
二、对象的成员访问
对象的成员访问有两种方式
- 对象.属性名; 这种方法最后JS引擎都会隐式转换为 对象[‘属性名’] 的方式
- 对象[‘属性名’];如果要访问的对象属性名是数字,那就只能通过这种方式来进行成员访问,且要主要中括号中的属性名必须是字符串格式才行
在使用 对象.属性名 进行成员访问的时候,JS引擎会隐式转换为 对象[‘属性名’] ```javascript // 对象的成员访问 // 对象的成员访问有两种方式 // + 对象.属性名 // + 对象[‘属性名’] 注意中括号中必须是字符串 var myLang = {
No1: 'HTML',No2: 'CSS',No3: 'JavaScript',myStudyingLang: function(num){console.log(this['No' + num]);}
}
myLang.myStudyingLang(1);
// 当对象的属性名是数字的时候,只能通过 对象['属性名'] 来进行成员访问;注意中括号中只能是字符串var obj = {name: 'kola',age: 18,10: 20}console.log(obj.name);// console.log(obj.10);console.log(obj['10']);// 在使用 对象.属性名 进行成员访问的时候,JS引擎会隐式转换为 对象['属性名']var person = {name: '小明',age: 19}// person.name 隐式转换为 => person['name']console.log(person.name);console.log(person['name']);
<a name="iYBGG"></a># 三、对象属性的枚举「遍历」for in- for in 不仅会遍历自身的属性,同时还会遍历**原型链上自定义的属性**「只有自定义的才会被遍历」- for in 会优先遍历数字属性,且会按照数字大小进行排序遍历- for in 也可以遍历数组,因为数组也是特殊的对象- 对象的成员访问:JS引擎在早期的对象成员访问的时候,其实是没有点语法的,obj.nam -> 最终都会隐式转换为obj['name']```javascript// 对象成员的枚举「遍历」for in// for in 不仅会遍历自身的属性,同时还会遍历原型链上自定义的属性「只有自定义的才会被遍历」// for in 会优先遍历数字属性,且会按照数字大小进行排序遍历// for in 也可以遍历数组,因为数组也是特殊的对象// 数组的遍历var arr = [1, 2, 3, 4];for(var i = 0; i < arr.length; i++){console.log(arr[i]);}var car = {brand: 'Benz',color: 'red',displacement: '3.0',lang: '5.0',width: '2.5'}for(var key in car){// console.log(key);// console.log(car.key); // 这样是不行的,car.key -> 会转换为 car['key'] 输出undefinedconsole.log('key' + ':' + car[key]);}
for in 不仅可以用来遍历对象,同时也可以用来遍历数组
// for in 不仅可以用来遍历对象,同时也可以用来遍历数组var arr = [11, 22, 33, 44];for(var i in arr){console.log(arr[i]);}
四、hasOwnProperty会排除原型链上的自定义属性
排除原型链上的自定义属性,只寻找对象自身的属性
// hasOwnProperty// 排除原型链上的自定义属性, 只寻找对象自身的属性var obj = {name: 'kola',age: 19,sex: 'femal',}Object.prototype.height = 175;console.log(obj.hasOwnProperty(obj.name)); // falseconsole.log(obj.hasOwnProperty(obj.height)); // false
for in 循环是会把原型链上的自定义属性全部循环出来的
现在我只想循环对象自身的属性
// hasOwnProperty: 排除原型链上的自定义属性,只寻找对象自身的属性var obj = {name: 'kola',age: 18}function Car(){this.brand = 'Benz';this.color = 'red';}Car.prototype = {lang: 5,width: 2.5}Object.prototype.name = 'Object';var car = new Car();for(var key in car){// for in 循环是会把原型链上的自定义属性全部循环出来的// 现在我只想循环对象自身的属性if(car.hasOwnProperty(key)){console.log(car[key]);}}
五、in 不会排除原型链的
in 是不会排除原型的
- ‘属性名’ in 对象
注意:属性名必须是字符串格式的
// in 是不会排除原型链的// '属性名' in 对象// 注意属性名必须是字符串function Car(){this.brand = 'Benz';this.color = 'red';}Car.prototype = {displacement : '3.0'}var car = new Car();// in 是不会排除原型链的// '属性名' in 对象; 注意此处的属性名必须是字符串// console.log(displacement in car);// console.log(color in car);console.log('displacement' in car);console.log('color' in car);
六、instanceof 判断对象是否是该构造函数实例化出来的
对象 instanceof 构造函数
// instanceof 判断某个对象是否是该构造函数实例化出来的function Car(){}var car = new Car();function Person(){}var person = new Person();console.log(car instanceof Car);console.log(car instanceof Object);console.log([] instanceof Array);console.log([] instanceof Object);console.log({} instanceof Object);
如何判断一个变量是否为数组
// 如何判断一个变量是否是数组var a = [];console.log(a.constructor);console.log(a instanceof Array);console.log(Object.prototype.toString.call(a));// Object原型上的toString方法通过改变其this指向可以实现数据类型检测「万能的」// Object.prototype = {// toString: function(){// this.toString();// }// }
真实项目中,如何判断一个变量是数组
// 真实项目中,如何判断一个变量是否是数组var arr = [];var toStr = Object.prototype.toString;var arrType = '[object Object]'if(toStr.call(arr)){console.log('是数组');}else{console.log('不是数组');}
七、this指向
函数内部的this默认指向的是window
- 预编译阶段函数中的this指向window
- 自执行函数中的this默认指向window
- 构造函数中的this指向实例化对象本身
- 可以通过call/apply/bind来改变this指向
- 箭头函数中是没有this的,而且箭头函数是没有原型的,所以不能被new执行
函数内部中的this默认指向window,预编译阶段函数中的this默认指向的是window
// this指向// + 函数内部的this默认指向的是window// + 预编译阶段函数中的this指向window// + 自执行函数中的this默认指向window// + 构造函数中的this指向实例化对象本身// + 可以通过call/apply/bind来改变this指向// 函数内部的this默认指向windowfunction func(b){this.d = 3;var a = 1;function c(){}}func(123);console.log(d);console.log(this.d);console.log(window.d);// AO = {// arguments: [123],// this: window,// b: undefined -> 123,// a: undefined -> 1,// c: function c(){}// }
构造函数中的this指向实例化对象本身
// 构造函数中的this指向实例化对象本身function Test(){// var this = {// name: 'kola',// __proto__: Test.prototype// }this.name = 'kola'// 系统默认隐式返回this// return this}var test = new Test();// GO = {// Test: function Test(){},// test: {// name: 'kola',// __proto__: Test.prototype// }// }// AO = {// name: 'kola',// __proto__: Test.prototype// }
call和apply可以更改this指向
// call和apply可以更改this指向function Person(name, age){this.name = '良雨';this.age = 38;}function Programmer(){Person.apply(this);this.work = 'programming';}var programmer = new Programmer();console.log(programmer);
7.1、this指向的总结
- 全局作用域下的this指向window
- 预编译阶段函数中的this指向window
- 函数内部中的this默认指向window
- 自执行函数中的this指向window
- 构造函数中的this指向实例化对象本身
- 可以通过call/apply/bind来改变this的指向
箭头函数中没有自己的this,而且构造函数没有原型,所以不能被new执行
八、callee和caller
callee,实参集合arguments在哪个函数里,就指向哪个函数
caller,返回当前被调用函数的函数引用;在被调用函数里面调用被调用函数的caller,就知道谁在调用它,必须调用了才有效
8.1、callee是arguments下的一个属性
callee是实参集合arguments下的一个属性;arguments在哪个函数里面就指向哪个函数
- arguments: 实参集合
- arguments.length: 函数的实参长度
- func.length: 函数的形参长度
arguments.callee.length: 函数的形参长度
// arguments: 实参集合// arguments.length: 函数的实参长度// func.length: 函数的形参长度// arguments.callee.length: 函数的形参长度function func(a, b, c){console.log(arguments.callee);console.log(arguments.callee.length);console.log(func.length);console.log(arguments.length);}func();
function test1(){console.log(arguments.callee); // 打印函数1本身function test2(){console.log(arguments.callee); // 打印函数2本身}test2();}test1();
8.2、用递归实现一个数的累加
```javascript
// 实现一个数的累加function sum(num){if(num <= 1){return 1;}return num + sum(num - 1);}var res = sum(10);console.log(res);
// 利用模块化编程模式实现一个数的累加var add = (function(num){if(num <= 1){return 1;}return num + arguments.callee(num - 1);})(100);console.log(add);
<a name="PSAFF"></a>## 8.3、caller返回当前被调用函数的函数引用- caller:返回当前被调用函数的函数引用```javascript// caller:返回当前被调用函数的函数引用function func1(){func2();function func2(){console.log(func2.caller);}}func1();
九、练习题
9.1、写出下面函数的运行结果
// 练习题function foo(){bar.apply(null, arguments);// bar.apply(arguments);// bar.call(arguments); -> bar(arguments)}function bar(){console.log(arguments);}foo(1, 2, 3, 4, 5);
9.2、typeof可能返回的值有那些
- ‘number’
- ‘string’
- ‘boolean’
- ‘undefined’
- ‘object’
-
9.3、实参集合和形参存在映射关系
// 形参和实参存在映射关系// 实参只有函数执行时传递了值,才可以在函数内部去修改function b(x, y, a){arguments[2] = 10;alert(a);}b(1, 2, 3);function b(x, y, a){a = 10;alert(arguments[2]);}b(1, 2, 3);
9.4、括号运算符练习「逗号运算符」
逗号运算符类似于逻辑运算符,它只返回逗号后面的最后一个
// 括号运算符练习// 逗号运算符类似于逻辑运算符,它只返回逗号后面的最后一个var f = (function f(){return '1';},function g(){return 2;});console.log(f);var f = (function f(){return '1';},function g(){return 2;});console.log(typeof(f()));
9.5、null和undefined练习题
- unll和undefined只有在两个等号的情况下才会相等,其余情况这个两个值和任何值都不相等
isNaN存在隐式类型转换
// 练习题// unll和undefined只有在两个等号的情况下才会相等,其余情况这个两个值和任何值都不相等console.log(null == undefined);console.log(null === undefined);// isNaN存在隐式类型转换console.log(isNaN('100'));console.log(parseInt('1a') == 1);
9.6、手写一个简单的isNaN
NaN和任何值都不相等,包括和它自己
isNaN(val); 此处的val必须是字符串格式的
// 手写一个简单的isNaNconsole.log(NaN == NaN);function isNaN(val){// isNaN存在隐式类型转换,会把val隐式转换为Number类型,然后再检测var res = Number(val) + '';if(res === 'NaN'){return true;}else{return false;}}console.log(isNaN('10'));console.log(isNaN('10a'));console.log(isNaN('width: 10px'));
9.7、空对象等于空对象吗?为什么?如何才能相等?
// 空对象等于空对象吗?// 为什么?// 如何让两个空对象相等?// console.log({} == {});var obj1 = {};var obj2 = obj1;console.log(obj1 == obj2);
9.8、this面试题
```javascript
// 面试题var a = '1';function test(){var a = '2';this.a = '3';console.log(a);}test();new test();console.log(a);
// 面试题var a = 5;function test(){a = 0;console.log(a);console.log(this.a);var a;console.log(a);}test();new test();
```
