ZhangYang's Blog

面向对象基础

面向对象

目的

  • 改善可读性
  • 提升重用性

原则(开放封闭原则)

  • 对于扩展是开放的(Open for extension)。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为
  • 对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码

要素

  • 封装
  • 继承
  • 多态

实例化对象

  • var object = new Object()
1
2
3
4
创建一个空对象作为 this
this.__proto__指向构造函数的prototype
运行构造函数
返回 this

手动指定对象的原型链

object.proto = {…}

  • 优点:简单直接
  • 缺点:这是ES6的方法,IE8以下不支持

借用 new

1
2
3
4
5
6
7
var myProto = {
name: 'foo'
}
var obj = {}
var Temp = function(){}
Temp.prototype = myProto
obj = new Temp()

使用Object.create(proto)

  • 以proto对象为原型,创建一个新的对象

对象_原型

面向对象(OOP)

  • Object oriented programming 的缩写,面向对象的程序设计,其中最重要的两个概念是类和对象

  • 类只是具备了某些功能和属性的抽象模型(类似于模具)用于做出成品(实例化对象),在JavaScript中,本身没有类的概念,我们需要用对象模拟出类,然后用类去创建对象

对象

  • 把类实例化后就是一个个对象,就像依照模具加工出一个个成品

面向对象有三个特性

  • 封装性:将一个类的使用和实现分开,只保留部分接口和方法与外部联系。
  • 继承性:子类自动继承其父级类中的属性和方法,并可以添加新的属性和方法或者对部分属性和方法进行重写。继承增加了代码的可重用性。
  • 多态性:子类继承了来自父级类中的属性和方法,并对其中部分方法进行重写

构造函数的方式创建一个拥有属性和方法的对象

  • 当 new 一个函数的时候,这个函数就会作为构造函数创建一个对象
  • 函数里面的 this 代表创建的这个对象。给 this添加属性就是给要创建的对象添加属性
  • 代码执行流程如下:
  • 创建一个空对象 person
  • 执行构造函数。里面的 this 代表person, 给person 添加属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Person(name,age){ //构造函数
this.name = name;
this.age = age;
//每个实例要重复一遍sayName ,大量对象存在的时候是浪费内存,
//可以把该方法放到prototype,作为公共方法,只需传入特定参数
this.sayName = function(){
console.log('I\'m ' + this.name)
}
}
/*也可单独在原型上这样写
Person.prototype.sayName = function(){
console.log('I\'m ' + this.name)
}
*/
var person = new Person('Mike','20');//创建一个对象,并传入参数
person.sayName(); //调用该对象的sayName方法,结果:I'm Mike
console.dir(person); //打印person对象的整套关系

images

原型

  • 任何函数在声明后都有一个属性 prototype,对应的值是一个对象叫原型对象
  • 当 new 这个函数的时候,会作为构造函数创建一个对象
  • 对象里面会有一个proto隐藏属性,指向上述构造函数原型对象
  • 当访问对象的属性时先从对象本身里找,找不到再从原型对象里找
1
2
3
4
5
6
7
8
function Student(name, sex){
this.name = name
}
Student.prototype.sayName = function(){
console.log(this.name)
}
var s1 = new Student('若愚')
s1.sayName()

images

特性

  • 用来实现基于原型的继承与属性的共享
  • 所有对象都有 proto,指向其构造函数的prototype(obj.proto === Object.prototype //true)
  • 构成原型链,同样用于实现基于原型的继承,例:访问一个对象的属性时,如果在obj中找不到,那么就会沿着proto依次查找,直到找到这个属性或null

constructor

  • 原型内的一个属性,指向它的构造函数
1
2
3
4
5
Student.prototype.constructor === Student
s1.constructor ==> s1.__proto__.constructor === Student
// 因为 s1.__proto__ === Student.prototype, 所以
// s1 instanceof Student === true

images

code:画出如下代码的原型图

1
2
3
4
5
6
7
8
9
10
11
12
13
function People (name){
this.name = name;
this.sayName = function(){
console.log('my name is:' + this.name);
}
}
People.prototype.walk = function(){
console.log(this.name + ' is walking');
}
var p1 = new People('后端');
var p2 = new People('前端');

images

code:创建一个 Car 对象,拥有属性name、color、status;拥有方法run,stop,getStatus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Car(name,color,status){
this.name = name;
this.color = color;
this.status = status;
}
Car.prototype.run = function(){
console.log(this.name + ' is running')
}
Car.prototype.stop = function(){
console.log(this.name + ' is stopped')
}
Car.prototype.getStatus = function(){
console,log(this.name + ' is ' + this.status)
}
var car = new Car('Mercedes','White',0);
car.run(); //Mercedes is running
car.stop(); //Mercedes is stopped
car.getStatus(); //0

code:创建一个GoTop对象,当new一个GotTop对象则会在页面上创建一个回到顶部的元素,点击页面滚动到顶部。拥有以下属性和方法

  • ct属性,GoTop 对应的 DOM 元素的容器
  • target属性, GoTop 对应的 DOM 元素
  • bindEvent 方法, 用于绑定事件
  • createNode 方法, 用于在容器内创建节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>go to top</title>
<style>
.ct {
background-color: #eee;
height: 1200px;
}
.ct .btn {
position: fixed;
right: 100px;
bottom: 100px;
}
</style>
</head>
<body>
<div class="ct">
</div>
<script src='https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js'></script>
<script>
function GoTop ($ct){
this.ct = $ct;
this.target = $('<button class="btn">GoTop</button>');
}
GoTop.prototype.creatNode = function(){
this.ct.append(this.target);
this.target.hide();
}
GoTop.prototype.bindevent = function(){
var _this = this;
$(window).on('scroll',function(){
if($(window).scrollTop() > 100){
_this.target.show(); //滚动大于100px,btn才出现
}else{
_this.target.hide();
}
})
this.target.on('click',function(){
$(window).scrollTop(0); //滚动到顶端
});
}
var gotop = new GoTop($('.ct'));
gotop.creatNode();
gotop.bindevent();
</script>
</body>
</html>

this原型链继承

this

  • this总是返回一个对象,简单说,就是返回属性或方法“当前”所在的对象
  • 只有在函数执行时才确定(变量是在定义就确定)
1
2
3
4
5
6
7
8
9
10
11
12
// this.name表示describe方法所在的当前对象的name属性
// 调用person.describe方法时,describe方法所在的当前对象是person,
// 所以就是调用person.name
var person = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
person.describe()
// "姓名:张三"

this的可变性

  • 由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// A.describe属性被赋给B,于是B.describe就表示describe方法所在的当前对象是B
// 所以this.name就指向B.name
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe()
// "姓名:李四"

this的三种使用场合

  • 全局环境
  • 构造函数
  • 对象的方法

全局环境

  • 在全局环境使用this,它指的就是顶层对象window
1
2
3
4
5
6
// 不管是不是在函数内部,只要是在全局环境下运行,this就是指顶层对象window
this === window // true
function f() {
console.log(this === window); // true
}

构造函数

  • 构造函数中的this,指的是实例对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义了一个构造函数Obj。由于this指向实例对象
// 所以在构造函数内部定义this.p,就相当于定义实例对象有一个p属性
// 然后m方法可以返回这个p属性
var Obj = function (p) {
this.p = p;
};
Obj.prototype.m = function() {
return this.p;
};
var o = new Obj('Hello World!');
o.p // "Hello World!"
o.m() // "Hello World!"

对象的方法

  • 当 A 对象的方法被赋予 B 对象,该方法中的this就从指向 A 对象变成了指向 B 对象
1
2
3
4
5
6
7
8
9
// obj.foo方法执行时,它内部的this指向obj
// 只有这一种用法(直接在obj对象上调用foo方法),this指向obj
var obj ={
foo: function () {
console.log(this);
}
};
obj.foo() // obj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 其他用法时,this都指向代码块当前所在对象(浏览器为window对象)
// 情况一
(obj.foo = function () {
console.log(this); //Window
})()
// 等同于
(function () {
console.log(this); //Window
})()
// 情况二
(false || function () {
console.log(this); //Window
})()
// 情况三
(1, function () {
console.log(this); //Window
})()

apply、call 、bind

  • JavaScript提供了call、apply、bind这三个方法,来切换/固定this的指向

apply

  • apply方法的作用与call方法类似,改变this指向,然后再调用该函数
  • 唯一的区别就是,它接收一个数组作为函数执行时的参数
1
2
3
4
5
fn.apply(context, [parm1, parm2, parm3])
/*调用函数,只接受2个参数,
第一个context是函数的执行上下文,内部的 this 指向 context
第二个是一个数组,里面包含传递进去的参数
*/
作用
  • 指定this值和参数(参数以数组或类数组对象的形式存在)的情况下调用某个函数。其实说白了用它可以绑定一个函数然后在另一个环境中(比如另一个函数中)使用新环境给的参数(指定this值、参数)进行运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// f函数本来接受两个参数,使用apply方法以后,就变成可以接受一个数组作为参数
function f(x,y){
console.log(x+y);
}
f.call(null,1,1) // 2
f.apply(null,[1,1]) // 2
// apply可以改变或者说扩展函数的作用域
var jubuColor={"color":"yellow"};
var quanjuColor={"color":"red"};
var window.color="green";
function showColor() {
console.log(this.color);
}
showColor.apply(jubuColor); // 绑定环境jubuColor
showColor.apply(quanjuColor); // 绑定环境quanjuColor
showColor.apply(window); // 绑定环境window
showColor.apply(this); // 绑定当前环境(对象)

call

  • 函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn.call(context, parm1, parm2, parm3)
/*调用函数,能接受多个参数,
第一个context是函数的执行上下文,内部的 this 指向 context
后面的都是传递进去的参数
*/
// call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象
var n = 123;
var obj = { n: 456 };
function a() {
console.log(this.n);
}
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456

bind

  • bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// counter.inc内部的this,默认指向counter对象
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
counter.count // 0
counter.inc()
counter.count // 1
// 如果将这个方法赋值给另一个变量,就会出错
// 函数func是在全局环境中运行的,这时inc内部的this指向顶层对象window
// 所以counter.count是不会变的,反而创建了一个全局变量count
// 因为window.count原来等于undefined,进行递增运算后undefined++就等于NaN
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc;
func();
counter.count // 0
count // NaN
// 为了解决这个问题,可以使用bind方法,将inc内部的this绑定到counter对象
// bind方法将inc方法绑定到counter以后,再运行func就会得到正确结果
var func = counter.inc.bind(counter);
func();
counter.count // 1

code:以下代码输出什么

explain

1
2
3
4
5
6
7
8
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func; //将func函数绑定到john对象的sayHi属性上,就可以调用sayHi方法
john.sayHi(); //John: hi! 调用john的sayHi方法,this就是john

code:下面代码输出什么

explain

1
2
3
4
5
6
7
8
9
10
11
12
13
func()
function func() {
alert(this)
}
/*window
原因:
this指的是,调用函数的那个对象,func 函数的调用对象为 window
--另一种解释--
func() 等价为 func.call(undefined)
当传入的 context 为 undefined 或 null 时,window 对象就是默认的 context
this 就是 window
*/

code:下面代码输出什么

explain

1
2
3
4
5
6
document.addEventListener('click', function(e){
console.log(this); //输出 #document,在事件处理程序中this指的是触发此事件的DOM对象
setTimeout(function(){
console.log(this); //输出window,setTimeout这个方法执行的函数this是全局对象
}, 200);
}, false);

code:下面代码输出什么

explain

1
2
3
4
5
6
7
8
9
10
11
12
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)
/*John
func 函数用call方法调用,第一个参数就是this,
func 函数内的 this.firstName 就是john.firstName,也就是 John
*/

code:以下代码有什么问题,如何修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指什么,this指$btn
this.showMsg(); //这个this指的是$btn
})
},
showMsg: function(){
console.log('前端');
}
}
/*会报错,因为代码中的 this.showMsg() 这个 this 是指 $btn 而不是对象 module,
$btn 上没有 showMsg 这个方法,所以会报错
*/

explain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//修改代码,把指向module对象的this保存起来
var module= {
bind: function(){
var _this = this //把指向module对象的this保存起来
$btn.on('click', function(){
console.log(this) //this指什么,this指$btn
_this.showMsg(); //_this为module对象
})
},
showMsg: function(){
console.log('前端');
}
}

原型链

  • 对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型本身也是对象,又有自己的原型,所以形成了一条原型链(prototype chain)

原型链的取值

1
2
3
4
5
6
7
8
9
10
11
// s1.toString() ==> s1.__proto__.toString()
// ==> s1.__proto__.__proto__.toString()
function Student(name, sex){
this.name = name
}
Student.prototype.sayName = function(){
console.log(this.name)
}
var s1 = new Student('若愚')
s1.sayName()
s1.toString()

images

原型链图

  • Student 是函数,Student 创建了 s1, Student.prototype == s1.proto
  • Student.prototype 是对象, Student.prototype 是由函数 Object 创建,所以 Student.prototype.proto === Object.prototype
  • 当获取一个对象的属性时,先从自己身上找==> 自己的 proto 对象上找 ==> proto.proto 上找, 一直到终点

images

code:有如下代码,解释Person、 prototype、proto、p、constructor之间的关联

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log('My name is :' + this.name);
}
var p = new Person("若愚")
p.sayName();
/*p.__proto__ === Person.prototype
p.__proto__.constructor === Person
Person.prototype.constructor === Person
*/

explain

  • Person是一个构造函数,本身也是一个对象
  • prototype是Person对象里面的一个属性,同时prototype是构造函数内部的原型对象,其拥有contructor和proto属性,其中contructor属性指向构造函数Person,proto指向该对象的原型
  • p是构造函数Person构造出来的示例,也拥有proto属性,p.proto === Person.prototype

images

code:上例中,对对象 p可以这样调用 p.toString()。toString是哪里来的? 画出原型图

explain

  • p.toString()方法,先从p的属性里面找,没有
  • 再从p.proto中找,还是没有
  • 再从p.proto.proto中找,找到了
  • 这样沿着proto这个链子一路找下去,就是原型链

images

code:对String做扩展,实现如下方式获取字符串中频率最高的字符

expalin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var str = 'ahbbccdeddddfg';
String.prototype.getMostOften = function(){
var obj = {}
var maxIndex = 0
//把字符串中的每个字符作为 obj 中的key,出现的次数作为value
for(var i=0; i<this.length; i++){
if(obj[this[i]]){
obj[this[i]]++
}else{
obj[this[i]] = 1
}
}
//找到最大的value,也就是最多出现了几次
for(var key in obj){
if(obj[key] > maxIndex){
maxIndex = obj[key]
}
}
//找到对应的key,也就是出现最多次数的字符
for(var key in obj){
if(obj[key]===maxIndex){
return [key, obj[key]]
}
}
}
var ch = str.getMostOften();
console.log(ch);
//["d", 5]

code:instanceof 有什么作用?内部逻辑是如何实现的?

explain

  • 判断是不是一个对象的实例,返回值是 true、false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var arr = []
obj = {}
arr instanceof Array //true
//内部逻辑为看实例的 __proto__ 是否为构造函数的 prototype 原型
arr.__proto__ === Array.prototype //true
//如果为 true ,则返回结果 true
arr instanceof Object //true
//内部逻辑为看实例的 __proto__ 是否为构造函数的 prototype 原型
arr.__proto__ === Object.prototype //false
//如果为 false ,则继续看下一层的 __proto__
arr.__proto__.__proto__ === Object.prototype //true
//如果为 true ,则返回结果 true
obj instanceof Array //false
//内部逻辑为看实例的 __proto__ 是否为构造函数的 prototype 原型
obj.__proto__ === Array.prototype //false
//如果为 false ,则继续看下一层的 __proto__
obj.__proto__.__proto__ === Array.prototype //false
//如果到最深一层的 __proto__ (最深一层为null)比较还是不相等,则返回 false
function isInstanceOf(obj, fn){
var oldpro = obj.__proto__;
do{
if(oldpro === fn.prototype){
return true;
}
else {
oldpro = oldpro.__proto__;
}
}while(oldpro)
return false;
}

继承

  • 继承就是子类拥有父类的属性和方法
  • 父类中是更加通用的属性和方法,通过继承,子类拥有父类的属性和方法,不需要重新去写这些重复的代码,提高了代码的重用性
  • 想要给所有子类修改属性和方法,只要在父类中修改就能“牵一发而动全身”,所有子类都修改了,提高了代码的可维护性
  • 直接给子类添加新的属性和方法,子类就会拥有这些属性和方法,表现出多态化,而父类不会被“污染”,提高了代码的独立性

继承实例

  • 人: 姓名,年纪,会说话,会走路
  • 程序员: 姓名, 年纪, 爱好, 会说话, 会走路,会写代码
  • 女程序员: 姓名, 年纪,爱好,会说话,会走路,会写代码,女

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function People(name, age){
this.name = name;
this.age = age;
}
People.prototype = {
walk: function(){
console.log(this.name + ' is ' + 'walking...');
},
say: function(){
console.lo(this.name + ' is ' + 'talking...' );
}
}
var people = new People('小明', 18);

程序员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Programmer(name, age, hobby){
this.name = name;
this.age = age;
this.hobby = hobby;
}
Programmer.prototype = {
walk: function(){
console.log(this.name + ' is ' + 'walking...');
},
say: function(){
console.lo(this.name + ' is ' + 'talking...' );
},
coding: function(){
console.log(this.name + ' is ' + 'coding...');
}
}

女程序员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function FemaleProgrammer(name, age, hobby, sex){
this.name = name;
this.age = age;
this.hobby = hobby;
this.sex = sex;
}
FemaleProgrammer.prototype = {
walk: function(){
console.log(this.name + ' is ' + 'walking...');
},
say: function(){
console.lo(this.name + ' is ' + 'talking...' );
},
coding: function(){
console.log(this.name + ' is ' + 'coding...');
},
buying: function(){
console.log(this.name + ' is ' + 'buying...');
}
}
  • 代码属性和方法重复,所有用继承来减少工作量

属性的继承

1
2
3
4
5
6
// 哪个对象调用了call,this就指向谁
function Programmer(name, age, hobby){
People.call(this, name, age);
this.hobby = hobby;
}
var programmer = new Programmer('小谷', 2, 'drive')

方法的继承

1
2
// Programmer.prototype.__proto__指向People.prototype
Programmer.prototype = Object.create(People.prototype);

继承后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function People(name, age){
this.name = name;
this.age = age;
}
People.prototype = {
walk: function(){
console.log(this.name + ' is ' + 'walking...');
},
say: function(){
console.lo(this.name + ' is ' + 'talking...' );
}
}
function Programmer(name, age, hobby){
People.call(this, name, age);
this.hobby = hobby;
}
Programmer.prototype = Object.create(People.prototype);
Programmer.prototype.constructor = Programmer;
Programmer.prototype.coding = function(){console.log('coding...')}
var p = new Programmer('小谷', 2, 'drive')
p.coding()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 兼容IE
function People(name, age){
this.name = name;
this.age = age;
}
People.prototype = {
walk: function(){
console.log(this.name + ' is ' + 'walking...');
},
say: function(){
console.log(this.name + ' is ' + 'talking...' );
}
}
function Programmer(name, age, hobby){
People.call(this, name, age);
this.hobby = hobby;
}
function inherit(superClass, subClass){
var temp = function(){}
temp.prototype = superClass.prototype
subClass.prototype = new temp()
subClass.prototype.constructor = subClass
}
inherit(People, Programmer);
Programmer.prototype.coding = function(){console.log('coding...')}
var p = new Programmer('小谷', 2, 'drive')

hasOwnProperty

  • 判断属性是不是自有属性
1
2
3
p.hasOwnProperty('hobby') //true
p.hasOwnProperty('coding') //false
p.hasOwnProperty('say') //false

code:下面两种写法有什么区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//方法1
function People(name, sex){
this.name = name;
this.sex = sex;
this.printName = function(){
console.log(this.name);
}
}
var p1 = new People('小明', 2)
//方法2
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.printName = function(){
console.log(this.name);
}
var p1 = new Person('小明', 27);

explain

  • 方法一创建的实例对象,其对象自身都会有一个相同功能的printName方法,会占用多余的内存空间
  • 方法二创建的实例对象,pringtName方法在Person的prototype上(相当于一个公共空间),每个实例对象可以沿原型链调用该方法,而不必创建一个相同的方法,节约了内存空间

code:Object.create 有什么作用?兼容性如何

explain

  • 创建一个以该对象为原型的实例
  • 兼容性:IE 9及以上支持
1
2
3
4
5
6
7
8
9
var me = {
name: "cg",
sayName: function(){
console.log("My name is " + this.name);
}
}
var p = Object.create(me)
//以 me 对象为原型,创建了实例 p
p.__proto__ === me //true

images

images

code:hasOwnProperty有什么作用? 如何使用

explain

  • 判断一个属性是否是自身的属性,返回 true 或 false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var People = function(name){
this.name = name
}
People.prototype.sayName = function(){
console.log("My name is " + this.name);
}
var p = new People("cg")
console.dir(p)
p.hasOwnProperty("name") //true
// name 是 p 自己的属性,所以返回true
p.hasOwnProperty("sayName") //false
// sayName 是 p 的原型 __proto__ 里的属性,不是自己的属性,所以返回false

images

code:如下代码中call的作用是什么

1
2
3
4
5
6
7
8
function Person(name, sex){
this.name = name;
this.sex = sex;
}
function Male(name, sex, age){
Person.call(this, name, sex); //这里的 call 有什么作用
this.age = age;
}

explain

  • call 的作用是实现了继承,Male 继承了 Person 的方法
  • call 改变了函数 Person 的执行上下文为 male对象

code:补全代码,实现继承

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name, sex){
this.name = name
this.sex = sex
}
Person.prototype.getName = function(){
console.log(this.name);
};
function Male(name, sex, age){
Person.apply(this, arguments)
this.age = age
}

explain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function Person(name, sex){
this.name = name
this.sex = sex
}
Person.prototype.getName = function(){
console.log(this.name);
};
function Male(name, sex, age){
Person.apply(this, arguments)
this.age = age
}
Male.prototype = Object.create(Person.prototype) //指定Male对象的原型为Person的原型,实现继承
//为了兼容 IE678 可以用以下三行代码实现继承,效果等同 Object.create
var Temp = function(){}
Temp.prototype = Person.prototype
Male.prototype = new Temp()
//修改 constructor
Male.prototype.constructor = Male
Male.prototype.getAge = function(){
console.log(this.age);
};
var xiaoming = new Male('小明', '男', 27);
xiaoming.getName(); //小明

面向对象组件

tab组件

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
* {
box-sizing: border-box;
}
.clearfix:after {
content: "";
display: block;
clear: both;
}
ul,
li {
list-style: none;
margin: 0;
padding: 0;
}
.tab {
width: 600px;
border: 1px solid #ccc;
margin: 50px auto;
}
.tab-header li {
float: left;
padding: 10px 20px;
border: 1px solid #ccc;
border-radius: 5px;
cursor: pointer;
}
.tab-header .active {
background: #ccc;
}
.tab-content {
background: #eee;
padding: 10px;
height: 200px;
}
.tab-content li {
display: none;
}
.tab-content li.see {
display: block;
}
</style>
</head>
<body>
<section class="tab">
<ul class="tab-header clearfix">
<li class="active">tab1</li>
<li>tab2</li>
<li>tab3</li>
</ul>
<ul class="tab-content">
<li class="see">内容1</li>
<li>内容2</li>
<li>内容3</li>
</ul>
</section>
<section class="tab">
<ul class="tab-header clearfix">
<li class="active">tab1</li>
<li>tab2</li>
<li>tab3</li>
<li>tab4</li>
<li>tab5</li>
</ul>
<ul class="tab-content">
<li class="see">内容1</li>
<li>内容2</li>
<li>内容3</li>
<li>内容4</li>
<li>内容5</li>
</ul>
</section>
<script>
var tab = (function(){
function _tab($target){
this.$target = $target;
this.init();
this.bind();
}
_tab.prototype.init = function(){
this.header = this.$target.find("ul").eq(0);
this.content = this.$target.find("ul").eq(1);
}
_tab.prototype.bind = function(){
var _this = this;
this.header.on("click", "li", function(){
var index = $(this).index();
_this.header.find("li").removeClass("active");
$(this).addClass("active");
_this.content.find("li").removeClass("see");
_this.content.find("li").eq(index).addClass("see");
})
}
return {
init: function($node){
$node.each(function(){
new _tab($(this));
})
}
}
})()
tab.init($(".tab"));
</script>
</body>
</html>

效果

images

懒加载组件

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<!DOCTYPE html>
<html lang="en">
<head>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<meta charset="UTF-8">
<title>Document</title>
<style>
ul,
li {
list-style: none;
margin: 0;
padding: 0;
}
.clearfix:after {
content: '';
display: block;
clear: both;
}
.pic {
width: 1240px;
margin: 0 auto;
}
.pic > li {
float: left;
border: 1px solid;
width: 400px;
height: 300px;
margin: 5px;
}
.pic > li > img {
width: 100%;
}
</style>
</head>
<body>
<ul class="pic clearfix">
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/1.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/2.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/3.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/4.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/5.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/6.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/7.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/8.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/9.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/10.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/11.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/12.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/13.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/14.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/15.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/16.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/17.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/18.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/19.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/20.jpg" alt=""></li>
<li><img src="" data-src="http://cdn.jirengu.com/book.jirengu.com/img/20.jpg" alt=""></li>
</ul>
<script>
var lazy = (function(){
function Exposure($target){
this.$target = $target;
this.bind();
this.loadImg();
}
Exposure.prototype.bind = function(){
var _this = this;
$(window).on("scroll", function() {
setTimeout(function(){
_this.loadImg();
},1500);
})
}
Exposure.prototype.loadImg = function(){
var _this = this
this.$target.each(function() {
if (_this.isVisible($(this)) && $(this).not(".show").length > 0) {
var url = $(this).data("src");
$(this).attr("src", url);
$(this).addClass("show");
}
})
}
Exposure.prototype.isVisible = function($node){
var $offset = $node.offset().top;
var $scrollTop = $(window).scrollTop();
var $height = $node.outerHeight();
var $windowHeight = $(window).height();
if ($windowHeight + $scrollTop > $offset && $scrollTop < $offset + $height) {
return true;
} else {
return false;
}
}
return {
init: function($node){
new Exposure($node);
}
}
})()
lazy.init($(".pic img"));
</script>
</body>
</html>

效果图

images

轮播组件

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
<style>
.clearfix:after {
content: '';
display: block;
clear: both;
}
ul,
li {
list-style: none;
margin: 0;
padding: 0;
}
.carousel {
position: relative;
width: 300px;
height: 300px;
margin:0 auto;
overflow: hidden;
}
.carousel > .img-ct {
position: absolute;
width: 1200px;
}
.carousel > .img-ct > li {
float: left;
width: 300px;
height: 300px;
}
.carousel > .img-ct > li > a > img {
width: 100%;
height: 100%;
}
.carousel > .btn {
position: absolute;
width: 40px;
height: 40px;
text-align: center;
line-height: 40px;
font-size: 1.5em;
color: #fff;
background-color: #666;
border-radius: 50%;
opacity: 0.6;
cursor: pointer;
}
.carousel > .btn-pre {
left: 10px;
top: 50%;
margin-top: -20px;
}
.carousel > .btn-next {
right: 10px;
top: 50%;
margin-top: -20px;
}
.carousel > .bullet {
position: absolute;
bottom: 50px;
width: 100%;
text-align: center;
}
.carousel > .bullet > li {
display:inline-block;
width: 30px;
height: 8px;
border: 1px solid #0ff;
border-radius: 50%;
background-color: #0ff;
margin: 0 2px;
}
.carousel > .bullet > .active {
background-color: #fff;
}
</style>
</head>
<body>
<div class="carousel">
<ul class="img-ct clearfix">
<li data-index="0"><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/1.jpg" alt="图片0"></a></li>
<li data-index="1"><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/2.jpg" alt="图片1"></a></li>
<li data-index="2"><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/3.jpg" alt="图片2"></a></li>
<li data-index="3"><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/4.jpg" alt="图片3"></a></li>
</ul>
<div class="btn btn-pre">&lt;</div>
<div class="btn btn-next">&gt;</div>
<ul class="bullet">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<div class="carousel">
<ul class="img-ct clearfix">
<li data-index="0"><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/4.jpg" alt="图片0"></a></li>
<li data-index="1"><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/3.jpg" alt="图片1"></a></li>
<li data-index="2"><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/2.jpg" alt="图片2"></a></li>
<li data-index="3"><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/1.jpg" alt="图片3"></a></li>
</ul>
<div class="btn btn-pre">&lt;</div>
<div class="btn btn-next">&gt;</div>
<ul class="bullet">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<script>
var Carousel = (function(){
function _Carousel($target){
this.$target = $target;
this.init();
this.bind();
}
_Carousel.prototype.init = function(){
var $imgCt = this.$target.find(".img-ct"),
$btnPre = this.$target.find(".btn-pre")
var $last = $imgCt.children("li").last().clone(),
$first = $imgCt.children("li").first().clone()
$imgCt.prepend($last);
$imgCt.append($first);
var $imgWidth = $imgCt.children("li").first().width(),
$imgLen = $imgCt.children("li").length
$imgCt.css({
width: $imgWidth*($imgLen),
left: -$imgWidth
})
}
_Carousel.prototype.bind = function(){
var _this = this
var $imgCt = this.$target.find(".img-ct"),
$btnPre = this.$target.find(".btn-pre"),
$btnNext = this.$target.find(".btn-next"),
$bullet = this.$target.find(".bullet"),
$curIndex = 0,
$imgLock = true
var $imgWidth = this.$target.find(".img-ct li").width(),
$imgLen = this.$target.find(".img-ct li").length
$btnPre.on("click", function(){
if($imgLock === false) return;
$imgLock = false;
pre(1);
})
$btnNext.on("click", function(){
if($imgLock === false) return;
$imgLock = false;
next(1);
})
$bullet.on("click", "li", function(){
$bulletIndex = $(this).index()
next($bulletIndex-$curIndex)
})
function next(idx){
$imgCt.animate({
left: "-="+idx*$imgWidth
},function(){
$curIndex += idx;
if($curIndex >= $imgLen-2){
$imgCt.css({left: -$imgWidth});
$curIndex = 0;
}
$imgLock = true;
setBullet();
})
}
function pre(idx){
$imgCt.animate({
left: "+="+idx*$imgWidth
},function(){
$curIndex -= idx;
if($curIndex < 0) {
$imgCt.css({left: -$imgWidth*($imgLen-2)});
$curIndex = $imgLen-2-1;
}
$imgLock = true;
setBullet();
})
}
function setBullet(){
$bullet.children("li").removeClass("active");
$bullet.children("li").eq($curIndex).addClass("active");
}
}
return {
init: function($node){
$node.each(function(){
new _Carousel($(this));
})
}
}
})()
Carousel.init($(".carousel"));
</script>
</body>
</html>

效果

images