JavaScript不区分类和实例的概念,而是经过原型来实现面向对象编程。
Java是从高级的抽象上设计的类和实例,而JavaScript的设计理念,听起来就比如Heros里的Peter,能够复制别人的能力。JavaScript就是别人的全部属性都拷贝过来,成为本身的一部分,并可以保留自身的能力。java
看廖老师的图片,应该就能感受出咋回事了,xiaoming这个实例把本身的__proto__指向Student就实现了继承,这种继承关系是脆弱的,也是动态能够修改的。编程
可是JavaScript是不推荐直接使用__proto__进行继承的。提供了一个Object.creat(原型)
来建立对象。这是JavaScript的一种原型继承的方法,以下实例。浏览器
var Student = { name : "robot", height:180, run:function(){ console.log(this.name + " is running"); }, grade:()=>"4"+"grade" }; function createStudent(name){ var s = Object.create(Student); // init new object s.name = name; return s; }; var xiaoming = createStudent("xiaosfsffsfsf");
当咱们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,若是没有找到,就到其原型对象上找,若是尚未找到,就一直上溯到Object.prototype对象,最后,若是尚未找到,就只能返回undefined。函数
以上说明JavaScript引擎有个追朔系统,优先使用当前域的进行查找,不行则向上一个原型内进行查找。工具
除了使用{...}进行建立对象,还可使用new 的方法,须要先定义一个构造函数,以下所示。若是使用new那么这个函数就会默认返回this这个对象,若是不是用new,直接调用,那么这个函数就返回undefined,像普通的函数同样。this
function Student(name){ this.name = name; this.hello = function(){ alert('hello'+name); } } var stu = new Student('XiaoMing');
新建立的stu的原型链是spa
stu ----> Student.prototype ----> Object.prototype ----> null
用new Student建立的对象stu,还从Student上继承了constructor属性,它指向Student自己。prototype
stu.constructor === Student.prototype.constructor; // true Student.prototype.constructor === Student; // true Object.getPrototypeOf(stu) === Student.prototype; // true stu instanceof Student; // true
这个原型链仍是盗用liaoxuefeng老师的图
能够看出实际上Student,xiaoming,xiaohong的原型都是指向Student.prototype。当前每一个对象的hello方法都是不一样的,属于不一样的对象。但根据方法查找规则,若是把hello放在Student.prototype上,就能够实现共用同一个方法,节省内存。即:设计
function Student(name) { this.name = name; } Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); };
另外,注意到构造函数里的属性,都没有通过var进行初始化,而是直接使用this.xxx进行绑定。因此若是没用new,而是直接调用构造函数,那么将会使this指向window,而后内部的各个属性都将添加到window上,无心中添加全局变量。而且在strict模式下,构造函数没有使用new进行调用,也会致使报错。code
***调用构造函数千万不要忘记写new。为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写,这样,一些语法检查工具如jslint将能够帮你检测到漏写的new。***
练习
function Cat(name) { this.name = name; } Cat.prototype.say = function(){ return "Hello, "+this.name+"!"; }
在此基础上,咱们还能够建立一个createCat()函数,在内部封装全部的new 操做。
function Cat(props) { this.name = props.name || '波斯猫'; this.color = props.name || '黑白'; } Cat.prototype.say = function(){ return "Hello, I am " + this.color + this.name+"!"; } function createCat(props){ return new Cat(props || {}); }
这样就不须要new 操做了,参数也很灵活,能够不传入,也能够传少许的,其余的属性将会由默认的值替代。并且参数不须要考虑顺序,可对收到的JSON直接生成对象。
JavaScript的原型继承实现方式就是:
定义新的构造函数,并在内部用call()调用但愿“继承”的构造函数,并绑定this;
借助中间函数F实现原型链继承,最好经过封装的inherits函数完成;
继续在新的构造函数的原型上定义新方法。
廖老师的一张图简单扼要说明这个继承模型。
其实3是不过重要的,由于ES6已经推出了class这个关键字来解决繁琐的原型链继承的复杂性,就像java同样好,但底层其实仍是原型链继承实现,这一点并无变化。
class Cat extends Animal{
constructor(name){ super(name); } say(){ return 'Hello, '+this.name+'!'; }
}
say()方法,实例依然是共享的。but,这个须要较新的浏览器支持,通常还用不了,可是可使用Babel这个库去兼容这个玩意,其实我不了解这是个啥库。