十三、对象属性遍历、this、caller\callee

十三、对象属性遍历、this、caller\callee

一、对象属性遍历

1.1 链式操作

问题:对象中有多个方法,想要在一个方法执行结束后,继续点语法调用下一个方法。

方式:在每个方法后面加上return this

解释:方法执行到最后一句,返回 this,而this指向对象,即返回对象;然后对象再用点语法调用方法,形成类似于链条的操作。

var sched = {wakeup: function(){console.log('Running');return this;},morning: function(){console.log('Go shopping');return this;},noon: function(){console.log('play basketball');return this;}
}sched.wakeup().morning().noon();

链式操作

1.2 对象调用属性的两种方式

1.2.1 点语法

var obj = {name: 'white'
}
console.log(obj.name); //'white'

1.2.2 [‘属性名’]

var myLang = {No1: 'HTML',No2: 'CSS',No3: 'JavaScript',myStudyLang: function(num){console.log(this['No' + num])}
}
myLang.myStudyLang(3); //'JavaScript'

最早js引擎对对象的处理:obj[‘属性名’]。

有了点语法以后,js引擎把obj.属性名 隐式转换->obj[‘属性名’]再处理。

总结:当对象访问属性时,js引擎处理方式最终都会变成obj[‘属性名’]的形式

1.3 对象枚举

js中的枚举说的是对象枚举

js中说枚举->遍历,遍历:在一种信息内一个一个获得其信息的过程。

1.3.1 数组枚举

js中数组是特殊的对象,使用for循环遍历。

1.3.2 对象枚举

使用for in循环遍历

var car = {brand: 'benz',color: 'red',displacement: '3.0'
}
for(var i in car){console.log(i + ':' + car[i]);
}
console.log('-----------')
for(var key in car){console.log(key + ':' + car.key);
}

对象枚举

为什么在for in循环中使用对象.属性名获取的数据是undefined?

因为car中没有一个具体的属性叫key;

总结使用场景

  • .语法:当我们想要获取对象某个确定的属性时,会使用点语法+属性名的形式调用对象的属性。

  • []语法:当我们不确定对象的属性名,或者想要获取对象的所有属性时,可以用[key]的形式,这里的key是一个变量,代表可变的字符串,用这个可变的字符串去对象中寻找对应的属性。

js引擎的处理过程:

1.car.key ->这里的key是对象的具体属性key -> car[‘key’] 而对象中没有一个属性名为字符串’key’->得到undefined,字符串’key’是定值

2.car[key]->这里的key是全局声明的key,key值随着遍历在改变

var car = {brand: 'benz',color: 'red',displacement: '3.0',key: '字符串key'
}
for(var i in car){console.log(i + ':' + car[i]);
}
console.log('-----------')
for(var key in car){console.log(key + ':' + car.key);
}

对象枚举2

for in 能遍历数组吗? √

var arr = [1, 2, 3, 4, 5]
for(key in arr){console.log(arr[key]);
}

可以这么理解:.语法是[]的语法糖,js在底层都是使用[]的形式去访问对象的属性。

开发使用.语法,解析时.语法->[ ]。

1.4 判断对象属性和对象归属

1.4.1 ※hasOwnProperty() 判断对象属性,排除原型

结论先行对象.hasOwnProperty('属性名')会排除原型链上的自定义属性,实例化对象的自身属性返回值为true。

基本用法:判断一个对象中是否存在某个属性,返回值为布尔值

var obj = {name: 'white',age: '22'
}
console.log(obj.hasOwnProperty('name')); //true

使用for in 遍历一个构造函数实例化的对象,且构造函数有原型:

function Car(){this.brand = 'benz',this.color = 'red'
}Car.prototype = {displacement: '2.0'
}
Car.prototype.name = 'white'var car = new Car();for(var key in car){console.log(key+': '+car[key]);
}for(var key in car){if(car.hasOwnProperty(key)){console.log('----' + key +': '+ scar[key]);}
}

hasOwnProperty_1

构造函数实例化的对象,在原型链上的自定义属性都会被遍历。

使用:console.log(对象.hasOwnProperty('属性名'))会排除原型链上的自定义属性,只打印实例化对象的属性。

1.4.2 in 判断对象属性,不排除原型

基本用法:'属性名' in 对象判断对象中有没有这个属性,不排除原型

var obj = {name: 'white',age: '22'
}
console.log('name' in obj); //true

1.4.3 ※ instanceof 判断对象来源

基本用法:判断对象是否由构造函数构造的

function Car(){}
var car = new Car();
console.log(car instanceof Car); //true
console.log(car instanceof Object); //trueconsole.log([] instanceof Array); //true
console.log([] instanceof Object); //trueconsole.log({} instanceof Object); //true

A instanceof B判断的逻辑:

A对象的原型链中有没有构造函数B的原型,即A对象的原型链上没有和构造函数B的原型重合。

1.5 判断接收的数据是不是一个数组

前端经常需要对后端传来的数据进行数据类型判断

会接收到什么样的数据呢?

  • null空

  • json字符串(json数组)

    [{name: 'abc'},{name: 'dbd'}
    ]
    
  • 一个对象中有错误的报错信息

    {"10061": 'error'
    }
    

如何判断接收的数据是不是一个数组呢?

1.5.1 寻找数据的构造器

ƒ Array() { [native code] }

var a = [1, 2, 3];
console.log(a.constructor); // ƒ Array() { [native code] }
console.log(a.constructor === a.__proto__.constructor); //true

1.5.2 使用 instanceof

会有一些问题

var a = [1, 2, 3];
console.log(a instanceof Array); //true

1.5.3 ※ Object.prototype.toString.call方法

尽可能用这种方法基本不用

var a = [1, 2, 3];
var str = Object.prototype.toString.call(a);
if(str === '[object Array]'){console.log('是数组');
}else {console.log('不是数组');
} //是数组

先缓存起来再用:

var a = [1, 2, 3];
var str1 = Object.prototype.toString,//通用方法trueTip = '[object Array]'
if(str1.call(a) === trueTip){console.log('数组');
}else {console.log('不是数组');
} //数组

1.通用方法:一个数据类型中有自己的方法,它的方法能被其他数据类型用apply/call来使用。

2.为什么Object.prototype.toString是[object xx]的形式:

因为使用typeof判断数据类型时,如果是引用值只能被判断成object,而Object.prototype.toString.call(x)能具体识别数据是[object object]还是[object Array]等,即能具体识别引用值是哪种引用值类型。

二、this指向

2.1 普通函数

普通函数未实例化,this默认指向window对象。

在函数内部书写this.d = 3相当于window.d = 3相当于不写var直接d = 3

function test(b){this.d = 3;var a = 1;function c(){}
}
test(123);
console.log(window.d); // 3
console.log(this.d); // 3
console.log(d); // 3

函数AO的变化

预编译阶段执行阶段
arguments: [123]this.d->window.d=3
this:window
b:undefined ->123
a:undefined1
c:function(){}

2.2 全局

全局中的this指向window对象。

var a = 1;
console.log(window.a); //1
console.log(this.a); // 1
console.log(a); // 1

2.3 构造函数

构造函数中的this指向实例化对象。

function Test(){this.name = '123';
}
var test = new Test();

全局GO的变化:

预编译阶段执行阶段
Test:function
test:undefinedtest:{__proto__: Test.prototype, name:‘123’}

构造函数AO的变化:

预编译阶段执行阶段(new的时候就执行了)
this:windowthis:{__proto__: Test.prototype, name:‘123’}

全局函数执行前,函数进行预编译阶段,此时AO产生,函数的预编译结束,函数开始执行,执行完毕AO完整,this指向了实例化对象,全局执行变量赋值,即把this指向的实力化对象赋值给了test,GO也完整了。

new的时候(函数执行的时候)在构造函数中相当于:

function Test(){/**var this = {*	__proto__: Test.prototype*}*/this.name = '123';
}

构造函数中this变化的过程:

  1. 全局函数执行前,函数进行预编译阶段,此时AO产生,this指向window对象。
  2. 函数的预编译结束,函数开始执行,this赋值了一个对象(即this指向了这个对象);对象中有了__proto__,随着执行给this增加了其他属性,最后默认return this
  3. 执行完毕,AO完整,全局执行变量赋值,即把this指向的实例化对象赋值给了test,GO也完整了。

2.4 apply/call更改this指向

​ 在一个构造函数函数中使用apply/call更改其他构造函数的this的指向,相当于借用其他构造函数的方法和属性。

function Person(name, age){this.name = name;this.age = age;
}function Programmer(name, age){Person.apply(this, [name, age]);this.work = 'Programming';
}var p = new Programmer('white',22,);
console.log(p);

apply

构造函数A中使用Person.apply后,实例化对象就有Person构造函数中的属性和方法了。

三、callee、caller

3.1 callee

arguments.callee返回正在执行的函数对象/返回实参列表对应的函数。

 function test(a,b,c){// 打印函数形参长度console.log(test.length); // 3// arguments.callee返回正在执行的函数对象console.log(arguments.callee.length); // 2// 打印实参的个数 console.log(arguments.length); //3}test(1,2);

在这里arguments.callee返回函数test。

3.1.1 那它有什么用呢:IIFE+递归

IIFE的函数执行时函数名会被JS引擎自动忽略掉。如果在IIFE中还想使用递归的方式调用函数本身,就需要使用callee来获取自身函数。

例如:递归的方式累加n位数

  • 全局函数方式

    function add(n){if(n <= 1){return 1;}return n + add(n - 1);
    }
    console.log(add(10)); //55
    
  • 全局变量接收IIFE结果

    var add1 = (function(n){if(n <= 1){console.log(arguments.callee);return 1; // 出口}return n + arguments.callee(n - 1);
    })(10);
    console.log(add1); //55
    

    在这里arguments.callee返回函数本身,并不需要函数名。函数后面 +()来执行。

    callee返回函数本身

一般会在IIFE处理数据,处理好以后立马赋值给一个变量,等待使用。

3.2 caller

返回当前被调用函数的函数引用;即哪个函数调用了我,我就返回哪个函数。

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

在test1中调用test2,打印test1的函数内容。

caller

不能在严格模式函数或用于调用它们的arguments对象上访问“caller”、“callee”和“arguments”属性

严格模式

作业

  • apply问题
function foo(){bar.apply(null, arguments)
}
function bar(){console.log(arguments);
}
foo(1, 2, 3, 4, 5);

函数()就相当于函数.call()相当于函数.call(null),apply和call的区别仅在于传参的格式,这里bar.apply(null, arguments)相当于调用bar函数,并把foo的arguments传入bar中;即打印foo的arguments列表。


  • JS的typeof可能返回的值有哪些?6种

object、number、string、boolean,undefined,function


  • 函数形实参映射问题:
function b(x, y, a){arguments[2] = 10;alert(a); //弹出提示框显示:10
}
b(1, 2, 3);
function b1(x, y, a){a = 10;alert(arguments[2]);//弹出提示框显示:10
}
b1(1, 2, 3);

函数形实参具有映射关系,即无论谁改变另一方都会随之改变。

a存在于栈中,arguments中的值存在于堆内存中,值的地址存在栈中。


  • ,运算符 + typeof
 var f = (function f(){return '1';},function g(){return 2;})();
console.log(typeof(f)); //'number'

(1,2,3)逗号运算符返回最后一个数据,无论它是什么类型的值;这里f = g()();即f的值为数字2。


  • 打印true的有哪些
console.log(undefined == null); // t
console.log(undefined === null); // f
console.log(isNaN('100')); // f
console.log(parseInt('1a') == 1); //t
console.log(NaN == NaN); //f
console.log(NaN === NaN);//f

对于undefined和null它们和0比较都为false,它们很像,但===要求两边一模一样,所以第一个为true,第二个为false;

isNaN(x),在判断以前,会Number(x),所以第三个相当于isNaN(100)为false;

parseInt()会从第一位数字开始取整,第四个parseInt(‘1a’)的值为1,所以为true。


  • isNaN()的判断相当于以下函数:
function isNaN1(num){var res = Number(num);if (res + '' == 'NaN'){return true;}else {return false;}
}
console.log(isNaN1(10)); //false
console.log(isNaN1('abc')); //true

NaN不与任何值相等。


  • 空对象等于空对象吗?
  • 为什么不等于
  • 怎么样才能等于
console.log({} == {}); //false

因为引用值对比的是地址;两个空对象存储在不同的空间里,地址不同。

怎么相等:

var obj = {};
var obj1 = obj;
console.log(obj == obj1); //true

  • 打印输出
var a = '1';
function test(){var a = '2';this.a = '3';console.log(a);
}
test(); //2
new test(); //2
console.log(a); //3

test()执行时,函数内部的a为’2’,this指向window,此时全局中的a为’3’,此时打印函数内部a为’2’;

new test()时,this指向实例化对象,打印函数中的a,而不是this.a打印值为’2’;

打印全局中的a时,已经在test()执行时,改成了’3’。


  • 打印结果
var a = 5;
function test(){a = 0;console.log(a);console.log(this.a);var a;console.log(a);
}
test();
new test();

test()时,打印0 5 0;函数预编译变量a提升,a赋值,this指向window,this.a为全局中的a;

new test()时,打印0 undefined 0;this指向实例化对象,但this.a未赋值。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部