考虑到内容太多问题,因此准备在学习深入理解过程中,分多次总结,本文续JavaScript深入理解(二)
类数组对象与Arguments
- 来源:类数组对象
类数组对象
- 定义:拥有一个 length 属性和若干索引属性的对象
- 区别:读写,长度,遍历都和数组一样,不同的是数组的API不能直接使用
- 更多:字符串其实也是类数组对象,可以通过下标访问,但是不能通过下标修改某个字符,仅可读。
- 大学刚学js的时候,就觉得字符串这个东西很奇怪,我竟然可以直接使用下标访问的方式访问某一个字符,纯数字确不可以,那时候没有类数组的概念,就勉强安慰自己字符串底层是数组的存储方式,实在有点逗哈。
- 使用数组方法
- 使用Function.call || Function.apply。
- 转换成数组
- splice
Array.prototype.splice.call(arrayLike, 0);
- slice
Array.prototype.slice.call(arrayLike);
- ES6 Array.from
Array.from(arrayLike);
- concat
Array.prototype.concat.apply([], arrayLike)
- ES6 ...运算符
function func(...arguments) { console.log(arguments); // [1, 2, 3] }
- splice
- Arguments 对象就是一个类数组对象。在客户端 JavaScript 中,一些 DOM 方法(document.getElementsByTagName()等)也返回类数组对象。
Arguments
- 定义:Arguments 对象只定义在函数体中,包括了函数的参数和其他属性。在函数体中,arguments 指代该函数的 Arguments 对象。
- length属性:实参的长度
- callee属性:通过它可以调用函数自身。但是严格模式下,此方法会报错。
还有这种操作!!!因为函数也是一种对象,可以函数添加自定义的属性。for (var i = 0; i < 3; i++) { (data[i] = function () { console.log(arguments.callee.i) }).i = i; }
- arguments 和对应参数是否共享?
- 传入的参数,实参和 arguments 的值会共享,当没有传入时,实参与 arguments 值不会共享
- 以上是在非严格模式下,如果是在严格模式下,实参和 arguments 是不会共享的。
- 应用
- 参数不定长
- 函数柯里化
- 递归调用
- 函数重载
创建对象的多种方式与优缺点分析
- 来源:对象创建
- 对象创建有多种方式,不能说谁优谁劣,关键是掌握分析优缺点的技巧,主要从以下一个方面考虑
- 是否导致对象无法识别,创建的实例使用 instanceof 能否指向构造函数,无法细分
- 对象创建原则
属性私有,方法共享
,方法是否重复创建 - 封装性好不好
- 理解字面量重写原型
Person.prototype.attr=object
扩充原型,不会导致原型丢失属性- Person.prototype = {}字面量重写原型,注意是否会导致缺失原本有的属性,比如constructor
- 组合模式分析
分析:function Person(name) { this.name = name; } Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } }; var person1 = new Person();
- 优点:该共享的共享,该私有的私有,使用
最广泛的方式
- 有的人就是希望全部都写在一起,即更好的封装性
- 优点:该共享的共享,该私有的私有,使用
- 使用动态原型模式时,不能用对象字面量重写原型,点击来源,看大神分析
继承的多种方式与优缺点分析
OO语言都支持两种继承方式: 接口继承
和 实现继承
.接口继承只继承方法签名,而实现继承则继承实际的方法.由于js中方法没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支持实现继承,而且其 实现继承 主要是依靠原型链
来实现的.
- 来源:对象继承
原型和实例
使用原型链后, 我们怎么去判断原型和实例的这种继承关系呢? 方法一般有两种.
- 第一种是使用 instanceof 操作符, 只要用这个操作符来测试实例(instance)与原型链中出现过的构造函数,结果就会返回true.
alert(instance instanceof Object);//true alert(instance instanceof Father);//true alert(instance instanceof Son);//true
- 第二种是使用 isPrototypeOf() 方法, 同样只要是原型链中出现过的原型,isPrototypeOf() 方法就会返回true。
alert(Object.prototype.isPrototypeOf(instance));//true alert(Father.prototype.isPrototypeOf(instance));//true alert(Son.prototype.isPrototypeOf(instance));//true
属性查找
- hasOwnProperty
使用了原型链后,当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,直到查找到达原型链的顶部 ,也就是 Object.prototype ,但是仍然没有找到指定的属性,就会返回 undefined。此时若想避免原型链查找,建议使用 hasOwnProperty 方法.。因为hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数
。console.log(instance1.hasOwnProperty('age'));//true
多种方式
- 原型链继承
Child.prototype = new Parent(); var child1 = new Child();
- 引用类型的属性被所有实例共享
- 在创建 Child 的实例时,不能向Parent传参
借用构造函数(经典继承)
function Child () { Parent.call(this); } var child1 = new Child();
- 避免了引用类型的属性被所有实例共享
- 可以在 Child 中向 Parent 传参
- 因为不存在原型链继承,方法必须都在构造函数中定义才能继承,因此每次创建实例都会创建一遍方法。
组合继承
function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function () { console.log(this.name) } function Child (name, age) { Parent.call(this, name); this.age = age; } Child.prototype = new Parent(); var child1 = new Child('kevin', '18');
- 优点:融合原型链继承和构造函数的优点,是 JavaScript 中
最常用的继承模式
。 - 缺点是会调用两次父构造函数。
- 优点:融合原型链继承和构造函数的优点,是 JavaScript 中
- 原型式继承
ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型。function createObj(o) { function F(){} F.prototype = o; return new F(); }
- 包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。
- 那么
原型链继承和原型式继承
的区别是什么咯?
原型式继承比原型链多了一层中转,是不是很熟悉?这一点在模拟bind的时候有提到过。
- 寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createObj (o) { var clone = Object.create(o); clone.sayName = function () { console.log('hi'); } return clone; }
- 缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
寄生组合式
- 组合继承最大的缺点是会调用两次父构造函数。那么不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype。便可以减少父构造函数使用。在组合模式上修改如下:
var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); var child1 = new Child('kevin', '18');
- 封装如下:
function object(o) { function F() {} F.prototype = o; return new F(); } function prototype(child, parent) { var prototype = object(parent.prototype); prototype.constructor = child; child.prototype = prototype; } // 当我们使用的时候: prototype(Child, Parent);
- 《JavaScript高级程序设计》中对寄生组合式继承的夸赞
这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
- 组合继承最大的缺点是会调用两次父构造函数。那么不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype。便可以减少父构造函数使用。在组合模式上修改如下:
几种原型继承理解
function Parent(){ this.name='lxq'; } Parent.prototype.sex='f'; function Child (age) { this.age = age; } // way 1 //Child.prototype=new Parent(); //var c=new Child(20); // 能访问自身属性,父对象属性,父对象原型链上的属性 //console.log(c.age,c.name,c.sex);//20 "lxq" "f" //way 2 //Child.prototype=Parent.prototype; //var c=new Child(20); // 父对象的属性不能访问到,因为直接原型链直接指向了父的prototype //console.log(c.age,c.name,c.sex);//20 undefined "f" //Child.prototype.value=1; // 会污染父prototype,子原型链上增加属性会影响到父元素原型链 //console.log(Parent.prototype.value);//1 //way 3 空对象中转,打破子原型链对父元素原型链的印象 function F(){} F.prototype=Parent.prototype; Child.prototype=new F(); var c=new Child(20); console.log(c.age,c.name,c.sex);//20 undefined "f" Child.prototype.value=1; console.log(Parent.prototype.value);//undefined
厉害了我的🐷