天天看点

ECMAScript-13 【对象遍历属性-this-caller-callee】

一、对象属性遍历

(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)总结

  1. 普通函数this → window
  2. 构造函数this → window
  3. apply/call改变this指向
  4. 实例化构造函数的时候this指向实例对象

三、callee/caller

(1)callee

function test1() {
    console.log(arguments.callee);
    function test2() {
        console.log(arguments.callee);
    }
    test2();
}
test1();
           

结果:

ECMAScript-13 【对象遍历属性-this-caller-callee】

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);
}
           

结果:

ECMAScript-13 【对象遍历属性-this-caller-callee】

返回当前被调用函数的函数引用

谁当前调用了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)
           

结果:

ECMAScript-13 【对象遍历属性-this-caller-callee】

第二个就相当于传入的参数是一个数组,所以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 
           

继续阅读