相信看到题目都知道,这些都是js千年不变的面试题。javascript
原型:至关于一个模具,用来生产实例对象。java
原型链:原型对象有个指针指向构造函数,实例对象又有一个指针指向原型对象,就造成了一条原型链,最终指向null。面试
原型:就是js里实现面向对象的方式,也就是说,js就是基于原型的面向对象。闭包
原型链:是js实现继承的方式。函数
做用域this
所谓做用域,就是变量或者是函数能做用的范围。prototype
那么JavaScript里有什么做用域呢?指针
一、全局做用域code
除了函数中定义的变量以外,都是全局做用域。对象
举个栗子:
var a = 10; function bar(){ console.log(a); } bar();//10
以上的a就是全局变量,处处能够访问a。
然鹅,
var a = 10; function bar(){ console.log(a); var a = 20; } bar();//undefined
什么鬼?undefined?
是的,你没看错。由于先搜索函数的变量看是否存在a,存在,又因为a被预解析(变量提高),提高的a绑定了这里的a做用域,因此结果就是undefined。
二、局部做用域
函数里用var声明的变量。
举个栗子:
var a = 10; function bar(){ var a = 20; console.log(a); } bar();//20
三、没有块级做用域(至ES5),ES6中有块级做用域
ES6以前,除了函数以外的块都不具有块级做用域。
常见的经典例子:
for(var i=0;i<4;i++){ setTimeout(function(){ console.log(i); },200); } //4 4 4 4
解决办法:
for(var i=0;i<4;i++){ (function(j){ setTimeout(function(){ console.log(j); },200); })(i) } //0 1 2 3
做用域链
变量随着做用长辈函数一级一级往上搜索,直到找到为止,找不到就报错,这个过程就是做用域链起的做用。
var num = 30; function f1(){ var num = 20; function f2(){ var num = 10; function f3(){ var num = 5; console.log(num); } f3(); } f2(); } f1();
闭包:js里为了实现数据和方法私有化的方式。内层函数能够调用外层函数的变量和方法。
若是有这样的需求
var go = function (a) { var str = 'go'; var add0 = function (a) { str += 'o'; return a ? str += a : add0;// 巧妙使用 } return a ? str += a : add0;// 巧妙使用 } console.log(go('l'));//gol console.log(go()('l'));//gool console.log(go()()('l'));//goool
既然前面说到继承的问题。继承指的是一个对象能够共享父级对象的一些属性。那么为何须要继承?好比上文的问题中,形状Shape有顶点这个属性,三角形和矩形均可以继承该属性而不须要再从新定义。那么就ES6之前跟ES6之后JavaScript中实现继承的问题来聊聊吧。
组合继承
function Parent(name, age) { this.name = name; this.age = age; } Parent.prototype.getName = function() { return this.name; } function child(name, age, sex) { Parent.call(this, name, age); this.sex = sex; } child.prototype = new Parent() var c1 = new child('zenquan', '23', 'M') console.log(c1.getName()) console.log(c1)
这种继承方式优势在于构造函数能够传参,不会与父类引用属性共享,能够复用父类的函数,可是也存在一个缺点就是在继承父类函数的时候调用了父类构造函数,致使子类的原型上多了不须要的父类属性,存在内存上的浪费。
寄生组合继承
function parent(name, age) { this.name = name; this.age = age; } parent.prototype.getName = function() { return this.name; } function child(name, age, sex) { parent.call(this, name, age); this.sex = sex; } child.prototype = Object.create(parent.prototype, { constructor: { value: child, enumerable: true, writable: true, configurable: true } }) var c1 = new child('zenquan', 23, 'M'); console.log(c1.getName()) console.log(c1)
以上继承实现的核心就是将父类的原型赋值给了子类,而且将构造函数设置为子类,这样既解决了无用的父类属性问题,还能正确的找到子类的构造函数。
ES6以后class继承
以上两种继承方式都是经过原型去解决的,在 ES6 中,咱们可使用
class
去实现继承,而且实现起来很简单
class parent { constructor(name, age) { this.name = name; this.age = age; } getName() { return this.name; } } class child extends parent { constructor(name, age, sex) { super(name, age); this.sex = sex; } } var c1 = new child('zenquan', 23, 'M'); console.log(c1);