引言
“对象”这个概念在编程中非常重要,任何语言和领域的开发者都应该具有面向对象思维,有效运用对象。良好的面向对象系统设计将是应用具有健壮性、可维护性和可扩展性的关键;反之,
如果面向对象环节有失误,那么项目将会面临灾难性的后果。
JavaScript面向对象的实质是基于原型的对象系统,而不是基于类。这是由最初的设计所决定的,
是基因层面的特点。随着ES Next标准的进化和新特性的添加,JavaScript 面向对象更加贴近其他传统面向对象型语言。有幸目睹语言的发展和变迁,伴随着某种语言成长,我认为是开发者之幸。
本篇就让我们深人对象和原型,了 解JavaScript 在这个方向上的能力。请注意,下面不会过多
赘述基础内容,读者需要具有一定的知识准备。
一、实现new没有那么容易
new关键字都做了什么
1.首先创建一个空对象a,这个对象会作为执行函数之后返回的对象实例
2.使对象a的原型(__proto__)指向构造函数的prototype属性
3.将对象a赋值给构造函数内部的this,并执行构造函数逻辑
4.根据构造函数执行逻辑,返回对象a或者构造函数的显示返回值
下面进行new实现
function newFn(...args){
const constructor = args.shift()
const obj = Object.create(constructor.prototype)
const result = constructor.apply(obj,args)
return (typeof result === 'object' && result != null) ? result : obj
}
解析:Object.creat()的作用是什么呢,就是把返回一个空对象a,对象a的__proto__指向第一个参数。使用apply把函数里的this指向空对象a,并执行函数体,返回值如果是个对象则返回,否则返回对象a
例子试验一下
function Dog(a){
this.a = a
}
Dog.prototype.add = function(){
this.a++
}
function Cat(a){
this.a = a
return {b: 10}
}
let d = newFn(Dog,5)
let c = newFn(Cat,5)
console.log(d); //{ a : 5 }
console.log(c); //{ b : 10 }
//Object.create() 例
const o = Object.create(Dog.prototype)
console.log(o.__proto__); //{ add: [Function (anonymous)] }
二、对象的继承
function Dog(a){
this.a = a
}
Dog.d = 'd'
Dog.prototype.add = function(){
this.a++
}
以上的对象有原型对象,有私有属性,还有实例属性,怎么都继承呢
原型链继承
function Cow(){
}
Cow.prototype = new Dog(5)
构造函数继承
function Cow(args){
//...
Dog.call(this, args)
}
let cow = new Cow(5)
组合继承
//XXX 组合继承
function Cow(a,b){
this.b = b
Dog.call(this, a)
}
Cow.prototype = Object.create(Dog.prototype)
Object.setPrototypeOf(Cow,Dog)
let cow = new Cow(10,15)
call方法使Cow实例继承Dog实例的属性和方法
Cow.prototype = Object.create(Dog.prototype) 使Cow的原型对象赋值为Dog的原型对象
Object.setPrototypeOf(Cow,Dog) 使Cow的__proto__指向Dog,这意味着Cow可以拿到Dog的静态属性和方法,例如变量d
封装成方法
function inherit(Child,Parent){
//继承原型
Child.prototype = Object.create(Parent.prototype)
//修复constructor
Child.prototype.constructor = Child
//存储超类
Child.super = Parent
//静态属性继承
if(Object.setPrototypeOf){
// setPrototypeOf es6
Object.setPrototypeOf(Child,Parent)
}else if(Child.__proto__){
// ES6引入__proto__
Child.__proto__ = Parent
}else{
// 兼容IE10,拷贝Child没有,Parent有的属性
for(var k in Parent){
if(Parent.hasOwnProperty(k) && !(k in Child)){
Child[k] = Parent[k]
}
}
}
}
三、jQuery中的对象思想
JQery的核心思想就是靠选择器获取node节点,声明一个空数组a,储存起来,数组a的__porto__赋值为JQery的函数对象$.fn,返回数组,所以数组可以使用JQery封装的方法,代码如下
var jQuery = (function(){
var $
$ = function(selector, context){
var dom = []
var nodeList
dom.__proto__ = $.fn
if(context !== undefined){
nodeList = context.querySelectorAll(selector)
}else{
nodeList = document.querySelectorAll(selector)
}
for(var i=0; i<nodeList.length; i++){
dom[i]=nodeList[i]
}
return dom
}
$.fn = {
css: function(...args){
let attr
if(args.length===1){
attr = args[0]
for(var i=0;i<this.length;i++)
{
for(var j in attr){
this[i].style[j]=attr[j];
}
}
}else if(args.length === 2){
let key = args[0]
let value = args[1]
for(var i=0;i<this.length;i++)
{
this[i].style[key]=value;
}
}
return this;
},
//...
}
$.ajax = function(){
//...
}
return $
})()
window.jQuery = jQuery
window.$ === undefined && (window.$ = jQuery)
总结:
面向对象是一个永远说不完的话题,更是一个永远不会过时的话题,具备良好的面向对象架构能力,对于开发者来说至关重要。同时,由于JavaScript面向对象的特殊性,使它区别于其他语言面向对象的特殊性,使它区别于其他语言而“与众不同”,所以我们在了解了JavaScript 原型、原型链知识的前提下,对比其他语言的思想来学习JavaScript 就变得非常重要和有意义了。