该笔记参考自:《你不知道的JavaScript上卷》
一.对象的遍历
1.for...in
循环可以用来遍历对象的可枚举属性名列表(包括[[Prototype]]
链)。
var arr = [1,2,3,4,5];
for(var i in arr){
console.log(i);
}
>>>
0
1
2
3
4
2.但是如何遍历属性的值?
var arr = [1,2,3,4,5];
for(var i = 0; i < arr.length; i++){
console.log(arr[i]);
}
>>>
1
2
3
4
5
- 这实际上只是通过遍历下标来指向值。
3.ES5
中增加了一些数组的辅助迭代器,包括了forEach()
、every()
和some()
。
//forEach()
var arr = [1,2,3,4,5];
arr.forEach(function(element,index,arr){
console.log(element);
});
>>>
1
2
3
4
5
//every()
var arr = [1,2,3,4,5];
var everyResult = arr.every(function(element,index,arr){
return element > 3;
});
everyResult;
>>>false
//some()
var arr = [1,2,3,4,5];
var someResult = arr.some(function(element,index,arr){
return element > 3;
});
someResult;
>>>true
①every()
和some()
的区别?
-
every()
会对数组中的每个元素执行回调函数,如果全部通过(true
),则返回true
。 -
some()
也会对数组中的每个元素执行回调函数,如果有任意一个通过(true
),则返回true
。
4.那么如何直接遍历值而不是数组下标呢?
- 用
for...of
循环呗(ES6
)
var arr = [1,2,3,4,5];
for(var v of arr){
console.log(v);
}
>>>
1
2
3
4
5
①for...of
循环的机制?
-
for...of
循环首先会被访问对象请求一个迭代器对象,然后通过调用迭代器对象的next()
方法来遍历所有返回值。
var arr = [1,2,3,4,5];
var element = arr[Symbol.iterator]();
element.next();
>>>{value: 1, done: false}
element.next();
>>>{value: 2, done: false}
element.next();
>>>{value: 3, done: false}
element.next();
>>>{value: 4, done: false}
element.next();
>>>{value: 5, done: false}
element.next();
>>>{value: undefined, done: true}
细节1:使用
ES6
的符号Symbol.iterator
来获取对象的@@iterator
内部属性。细节2:
@@iterator
本身并不是一个迭代器对象,而是一个返回迭代器对象的函数。
5.遍历普通的对象
①既然for...of
循环那么强大,那么适用于普通的对象吗?
- NO!
②为啥?
- 普通的对象没有内置的
@@iterator
。
③喔,那照这么说,我手动加一个行不行呢?
- No problem.
var person = {
name: 'Gerg',
age: 21,
[Symbol.iterator]: function(){
var o = this;
var index = 0;
var ok = Object.keys(o);
return {
next: function(){
return{
value: o[ok[index++]],
done: (index > ok.length)
};
}
};
}
};
var element = person[Symbol.iterator]();
element.next();
>>>{value: "Gerg", done: false}
element.next();
>>>{value: 21, done: false}
element.next();
>>>{value: undefined, done: true}
④当普通对象设置了@@iterator
后,就可以放心使用for...of
循环了。
var person = {
name: 'Gerg',
age: 21,
[Symbol.iterator]: function(){
var o = this;
var index = 0;
var ok = Object.keys(o);
return {
next: function(){
return{
value: o[ok[index++]],
done: (index > ok.length)
};
}
};
}
};
for(var v of person){
console.log(v);
}
>>>
Gerg
21
⑤还可以使用Object.defineProperty()
定义@@iterator
喔
var person = {
name: 'Gerg',
age: 21
};
Object.defineProperty(person,Symbol.iterator,{
enumerable: false,
writable: false,
configurable: true,
value: function(){
var o = this,
index = 0,
ok = Object.keys(o);
return{
next: function(){
return{
value: o[ok[index++]],
done: (index > ok.length)
};
}
};
}
});
var element = person[Symbol.iterator]();
element.next();
>>>{value: "Gerg", done: false}
element.next();
>>>{value: 21, done: false}
element.next();
>>>{value: undefined, done: true}
for(var v of person){
console.log(v);
}
>>>
Gerg
21
二.混合对象"类"
1.类理论
①什么是类?String
对象就是一个类。
②什么是实例?'Gerg'
字符串就是String
类的一个实例。
③什么是继承?实例或类可以使用另一个类中的方法。
'Gerg'.length;
>>>4
'Gerg'.indexOf('e');
>>>1
④什么是多态?父类的通用行为可以被子类更为特殊的行为重写。
2.在软件设计中类是一种可选的模式,是否在JavaScript
使用决定权在你。
3.类的继承
①定义好一个子类后,相对于父类来说它就是一个独立并且完全不同的类。
②子类会包含父类行为的原始副本,但是也可以重写所有继承的行为甚至定义新的行为。
③父类和子类并非实例。
④JavaScript
中父类与子类的关系只存在于两者构造函数对应的.prototype
对象中,因此它们的构造函数之间并不存在直接联系。
⑤多态并不表示子类与父类有关联,子类得到的仅仅是父类的一份副本。类的继承实际上就是复制。
⑥声明是super
?
子类相对引用父类中的行为,子类得到的仅仅是继承自父类行为的一份副本。子类对继承到的一个方法进行"重写",并不会影响父类中的方法,这两个方法分别作为独立的存在。正是因为这样才能使用相对多态引用访问父类中的方法。
4.多重继承
①多重继承意味着所有父类的定义都会被复制到子类中。
②钻石问题?
子类
D
继承自两个父类(B
和C
),这两个父类都继承自A
。如果A
中有drive()
方法并且B
和C
都重写了这个方法(多态),那么D
引用drive()
时到底应该选择哪个版本呢(B:drive()
还是C:drive()
)?目前的
JavaScript
:它本身并不提供"多重继承"功能。但这无法阻止开发者们的热情。
5.混入
①JavaScript
中的函数无法真正地复制,所以只能复制对共享函数对象的引用。
②寄生继承
function Vehicle(){
this.engines = 1;
}
Vehicle.prototype.ignition = function(){
console.log('Turing on my engine.');
};
Vehicle.prototype.drive = function(){
this.ignition();
console.log('Steering and moving forward!');
};
function Car(){
var car = new Vehicle();
car.wheels = 4;
var vehDrive = car.drive;
//重写Vehicle中的drive()函数
car.drive = function(){
vehDrive.call(this);
console.log('Rolling on all ' + this.wheels + ' wheels!');
};
return car;
}
var myCar = new Car();
myCar.drive();
>>>
Turing on my engine.
Steering and moving forward!
Rolling on all 4 wheels!
- 细节:
调用
new Car()
时会创建一个新对象并绑定到Car
的this
上。但是这里我们并没有使用这个对象而是返回了我们自己的car
对象,所以最初被创建的这个对象会被丢弃。
③显式混入
function mixin(sourceObj,targetObj){
for(var key in sourceObj){
//只赋值sourceObj中有,而targetObj中无的属性
if(!(key in targetObj)){
targetObj[key] = sourceObj[key];
}
}
return targetObj;
}
var Vehicle = {
engines: 1,
iginition: function(){
console.log('Turing on my engine.');
},
drive: function(){
this.iginition();
console.log('Steering and moving forward!');
}
};
var Car = mixin(Vehicle,{
wheels: 4,
carDrive: function(){
Vehicle.drive.call(this);
console.log('Rolling on all ' + this.wheels + ' wheels!');
}
});
Car.carDrive();
>>>
Turing on my engine.
Steering and moving forward!
Rolling on all 4 wheels!
④隐式混入
var Something = {
cool: function(){
this.greeting = 'Hello World';
this.count = this.count ? this.count + 1 : 1;
}
};
Something.cool();
Something.greeting;
>>>"Hello World"
Something.count;
>>>1
var Another = {
anotherCool: function(){
Something.cool.call(this);
}
};
Another.anotherCool();
Another.greeting;
>>>"Hello World"
Another.count;
>>>1
- "借用"了函数
Something.cool()
并在Another
的上下文中调用了它。
6.小结
①类意味着复制。
②多态(在继承链的不同层次上,同名却功能不同的函数)看上去像是从子类引用父类,但是本质上引用的其实是复制的结果。
③对象和函数只能复制引用,无法复制被引用的对象或函数本身。