一、对象属性遍历
(1)链式操作
var sched = {
wakeup: function () {
console.log('Running');
return this; // 把this指向的对象返回出去
},
morning: function () {
console.log('Going shopping');
return this;
},
noon: function () {
console.log('Having a rest');
return this;
},
afternon: function () {
console.log('Studying');
return this;
},
evening: function () {
console.log('Walking');
return this;
},
night: function () {
console.log('Sleeping');
return this;
}
}
sched.wakeup().morning().noon().afternon().evening().night()
当一个对象执行里面的方法以后再返回这个对象,这样的操作就叫做链式操作
(2)中括号访问对象属性
var myLang = {
No1: 'HTML',
No2: 'CSS',
No3: 'JavaScript',
myStudingLang: function (num) {
console.log(this['No' + num]) // 字符串拼接属性名
}
}
myLang.myStudingLang(2); // CSS
console.log(myLang['No1']); // HTML
最早的JS引擎 obj[‘name’]
有点语法以后 obj.name → obj[‘name’] 隐式的转换
(3)对象枚举
枚举: 一组有共同特性数据的集合,JavaScript的枚举实际上就是对象
枚举 → 遍历 (遍历是按顺序一个一个去获取一组信息的过程), 在Javascript当中有枚举就有遍历,遍历就有枚举,二者是相辅相成的
(4)遍历数组
在JavaScript当中数组是特殊的对象
1.for循环
var arr = [1, 2, 3, 4, 5];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
2.for…in…
for (var i in arr) {
console.log(arr[i]);
}
i 是索引号
(5)遍历对象
1.for…in…
var car = {
brand: 'Benz', //哈希表:key : value
color: 'red',
displacement: '3.0',
lang: '5',
width: '2.5'
}
for (var key in car) {
console.log(key + ':' + car[key]); // 这样写就不会转换,key对应的键名也会传进来
}
key是字符串类型的属性名
注意:
for…in的遍历一定要写中括号,否则用点语法会隐式转换,就会查找出错
for (var key in car) {
console.log(car.key); // 隐式转换 car.key → car['key'] → undefined
}
JS引擎会误认为要访问key的属性,就会把car.key转换成car[‘key’],car里面没有key属性所以打印undefined
(6)hasOwnProperty
排除原形上的属性
自身有这个属性就返回true,没有就返回false
var obj = {
name: '艾小野',
age: 32
}
console.log(obj.hasOwnProperty('name')); // true
console.log(obj.hasOwnProperty(obj.name)); // false
function Car() {
this.brand = 'Benz';
this.color = 'red';
this.displacement = '3.0';
}
Car.prototype = {
lang: 5,
width: 2.5
}
Object.prototype.name = 'Object'
var car = new Car();
for (var k in car) {
console.log(k + ':' + car[k]); // brand:Benz,color:red,displacement:3.0,lang:5,width:2.5,name:Object
}
当只想要遍历对象的属性,排除原形的属性的时候,就可以用hasOwnProperty
for (var key in car) {
if (car.hasOwnProperty(key)) { // 注意括号里面是属性名,而不是key.***
console.log(car[key]); // Benz,red,3.0
}
}
hasOwnProperty就是排除原型上面的自定义的属性,但in不排除
(7)in判断属性
var car = {
brand: 'Benz',
color: 'red'
}
console.log('displacement' in car); // false 注意必须是字符串
// 隐式转换成car['displacement']
function Car() {
this.brand = 'Benz';
this.color = 'red';
}
Car.prototype = {
displacement: '3.0'
}
var car = new Car();
console.log('displacement' in car); // true
与hasOwnProperty不同的是,in不会排除原型上面的自定义的属性
(8)instanceof
判断这个对象是否是该构造函数实例化出来的
function Car() { }
var car = new Car();
function Person() { }
var p = new Person();
console.log(car instanceof Car); // true
console.log(p instanceof Car); // false
console.log(car instanceof Object); // true
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true
console.log({} instanceof Object); // true
Person.prototype = Car.prototype
var p = new Person();
console.log(p instanceof Car); // true
当A对象的原形是B构造函数的原形的时候,那么A就构造函数B制造的
(9)判断引用数据类型的方法
// 方法1
var a = [] || {}
console.log(a.constructor); // ƒ Array() { [native code] }
// 方法2
console.log(a instanceof Array); //true
// 方法3(常用)
var str = Object.prototype.toString.call(a)
console.log(str); // [object Array] 引用类型的数组
具体用法
var str = Object.prototype.toString,
trueTip = '[object Array]';
if (str.call(a) === '[object Array]') {
console.log('是数组');
} else {
console.log('不是数字');
}
可以用str、trueTip将数据线缓存起来,需要的时候就直接.call()就可以了
二、构造函数的this指向
(1)普通函数的this
// 问1:函数内部的this指向谁?
function test(b) {
this.d = 3; // window.d = 3
var a = 1;
function c() { }
}
test(123);
AO = {
arguments: [123],
this: window,
b: 123,
a: 1,
c: function c() { }
}
console.log(this.d);
console.log(window.d);
// 在函数内部,只要你没实例化这个函数,this就指向的是window
// 在全局范围内,this也是指向window的
(2)构造函数的this
function Test() {
var this = {
__proto__: Test.prototype
}
this.name = '123'
// return this
}
var test = new Test();
// 没实例之前:
AO = {
this: window
}
// 实例化后:
AO = {
this: {
name: '123',
__proto__: Test.prototype
}
}
GO = {
Test: function Test() {...},
test: {
name: '123',
__proto__: Test.prototype
}
}
(3)call/apply
function Person() {
this.name = '张三';
this.age = 18
}
function Programmer() {
Person.apply(this); // 把this指向Programmer实例化出来的对象
this.work = 'Programming';
}
var p = new Programmer();
console.log(p);
// 问1:apply什么时候需要传值?
// 答:当借用的构造函数是有参数的,就需要传值
(4)总结
- 普通函数this → window
- 构造函数this → window
- apply/call改变this指向
- 实例化构造函数的时候this指向实例对象
三、callee/caller
(1)callee
function test1() {
console.log(arguments.callee);
function test2() {
console.log(arguments.callee);
}
test2();
}
test1();
结果:
callee在哪个函数里面就返回哪个函数
用callee的方式做递归累加n位数
// 用函数名调用函数
function sum(n) {
if (n <= 1) {
return 1;
}
return n + sum(n - 1);
}
var res = sum(10);
console.log(res);
// 用callee调用函数(推荐写法)
var sum = (function (n) {
if (n <= 1) {
return 1;
}
return n + arguments.callee(n - 1);
})(10)
console.log(sum);
当用立即执行函数的做闭包时候,可以用callee的方式来回调函数,这样当有变量名与函数名重名时,就不会发生冲突
(2)caller
在严格模式下,caller/callee这些arguments属性都不可以都不可以通过
test1()
function test1() {
test2();
}
function test2() {
console.log(test2.caller);
}
结果:
返回当前被调用函数的函数引用
谁当前调用了test2就返回哪一个函数
四、面试题
(1)请问打印出来是什么?
function foo() {
bar.apply(null, arguments); // null就是不改变this指向,这样写就相当于执行bar
bar.call(null, arguments)
}
function bar() {
console.log(arguments);
}
foo(1, 2, 3, 4, 5)
结果:
第二个就相当于传入的参数是一个数组,所以arguments的第0位是数组,而第一个的实参本身就是一个数组,所以正常打印
(2)JS的typeof可能返回的值有哪些?
string、number、boolean、undefined、function、object(包括null,这是因为历史遗留问题)
(3)a是多少?
function b(x, y, a) {
arguments[2] = 10;
alert(a);
}
b(1, 2, 3) // 10
function b(x, y, a) {
a = 10;
alert(arguments[2]);
}
b(1, 2, 3) // 10
(4)f打印出来是什么?
var f = (
function f() {
return '1';
},
function g() {
return '2';
}
)
console.log(typeof (f)); // 'function' f → f(g)
var f = (
function f() {
return '1';
},
function g() {
return 2;
}
)()
console.log(typeof (f)); // 'number'
逗号运算符返回最后一个
(5)这四个里面打印true的有哪些?
console.log(undefined == null); // true
console.log(undefined === null); // false
console.log(isNaN('100')); //false
console.log(parseInt('1a') == 1); // true
(6)下面的代码有问题吗?
function isNaN1(num) {
var res = Number(num);
console.log(res); // 1
if (res == NaN) {
return true;
} else {
return false; // false
}
}
console.log(isNaN1(1));
有问题,因为NaN不等于任何东西,包括自己,当传入的值是一个非数的时候,NaN 不等于 NaN
修改后:
function isNaN1(num) {
var res = Number(num) + '';
console.log(res); // 'NaN'
if (res == 'NaN') {
return true; // true
} else {
return false;
}
}
console.log(isNaN1('a'));
(7)空对象等于空对象吗?
不等,因为引用值对比的是地址,两个不同的空对象存储在不同的空间里面,地址就不相同
让它们相等的办法:
var a = {};
var b = a;
console.log(a == b);
(8)下列分别打印出来是什么?
var a = '1';
function test() {
var a = '2';
this.a = '3'; // → window.a
console.log(a);
}
test(); // 2
new test();
console.log(a); // 3
(9)下列分别打印出来是什么?
var a = 5;
function test(){
a = 0;
console.log(a);
console.log(this.a);
var a;
console.log(a);
}
test() // 0 5 0
new test() // 0 undefined 0