1.什么是对象
万物皆对象
对象是单个事物的抽象,一本书、一辆汽车、一个人都可以是对象。对象是一个容器,封装了属性(property)和方法(method),属性是对象的状态,方法是对象的行为(完成某种任务)。
什么是面向对象
1)面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。
2)面向对象编程 —— Object Oriented Programming ,简称 OOP ,是一种编程开发思想。
3)面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
// 创建Student类
function Student(name, score) {
// this 实例化(创建对象)
// 给属性赋值
this.name = name;
this.score = score;
}
// 在原型上添加方法
Student.prototype.printScore = function () {
console.log(this.name + ':' + this.score);
};
var stu1 = new Student('张三', 80);
var stu2 = new Student('李四', 100);
console.log(stu1, stu2);
stu1.printScore();
stu2.printScore();
</script>
</html>
2.工厂函数
写一个函数,解决代码重复问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
// 工厂函数:写一个函数,解决代码重复问题
function createPerson(name, age) {
return {
name: name,
age: age,
sayName: function () {
console.log(this.name);
},
};
}
var pa = createPerson('张三', 18);
var pb = createPerson('李四', 20);
console.log(pa, pb);
</script>
</html>
工厂函数(二)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
// 固定值
this.type = 'human';
this.sayHello = function () {
console.log('hello ' + this.name);
};
}
Person.prototype.sayName = function () {
console.log(this.name);
};
// new 的是构造函数:
// 构造函数:实例化对象 初始化属性
var pa = new Person('张三', 18);
var pb = new Person('李四', 20);
console.log(pa, pb);
// 构造函数判断类型
console.log(pa.constructor === Person); // => true
console.log(pb.constructor === Person); // => true
console.log(pa.constructor === pb.constructor); // => true
// 原型判断类型
console.log(pa instanceof Person); // true
console.log(pa.sayName === pb.sayName); // true
console.log(pa.sayHello === pb.sayHello); // false
</script>
</html>
3.构造函数
构造函数是根据具体的事物抽象出来的抽象模板。使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题,对于这种问题我们可以把需要共享的函数定义到构造函数外部:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function sayHello() {
console.log('hello ' + this.name);
}
function Person(name, age) {
this.name = name;
this.age = age;
// 固定值
this.type = 'human';
this.sayHello = sayHello;
}
Person.prototype.sayName = function () {
console.log(this.name);
};
var pa = new Person('张三', 18);
var pb = new Person('李四', 20);
console.log(pa.sayHello === pb.sayHello); // true
</script>
</html>
4.原型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.type = 'human';
Person.prototype.sayName = function () {
console.log(this.name);
};
Person.prototype.sayHello = function () {
console.log(this.name + ':hello');
};
var pa = new Person('张三', 18);
var pb = new Person('李四', 20);
console.log(pa, pb);
console.log(pa.sayHello === pb.sayHello); // true
</script>
</html>
5.构造函数、实例、原型三者之间的关系
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function F() {}
console.log(F.prototype);
F.prototype.sayHi = function () {
console.log('hi');
};
var f = new F();
// F.prototype 原型对象 f.__proto__ 原型属性
console.log(F.prototype === f.__proto__);
// 对象调用原型上的方法
f.sayHi();
</script>
</html>
总结:(1)任何函数都具有一个 `prototype` 属性,该属性是一个对象
(2)构造函数的 `prototype` 对象默认都有一个 `constructor` 属性,指向 `prototype` 对象所在函数
(3)通过构造函数得到的实例对象内部会包含一个指向构造函数的 `prototype` 对象的指针 `__proto__`
(4)所有实例都直接或间接继承了原型对象的成员
6.原型简写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
// 重写prototype
Person.prototype = {
// 指定构造函数
constructor: Person,
type: 'human',
sayName: function () {
console.log(this.name);
},
sayHello: function () {
console.log('hello');
},
};
var pa = new Person('张三', 18);
var pb = new Person('李四', 20);
console.log(pa, pb);
</script>
</html>
存在问题:
如果真的希望可以被实例对象之间共享和修改这些共享数据那就不是问题。但是如果不希望实例之间共享和修改这些共享数据则就是问题。
7.扩展原型方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
Array.prototype.sayHi = function () {
console.log('hi');
};
var a = new Array();
a.sayHi();
console.log(a);
</script>
</html>
8.继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function Person(name, age) {
this.type = 'human';
this.name = name;
this.age = age;
}
function Studnet(name, age, score) {
Person.call(this, name, age);
this.score = score;
}
var s = new Studnet('zhangsan', 18, 100);
console.log(s);
</script>
</html>
9.拷贝继承(for-in)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function Person(name, age) {
this.type = 'human';
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person,
sayHi: function () {
console.log('hi');
},
};
function Studnet(name, age, score) {
Person.call(this, name, age);
this.score = score;
}
// copy Person的原型
for (var key in Person.prototype) {
Studnet.prototype[key] = Person.prototype[key];
}
var s = new Studnet('zhangsan', 18, 100);
console.log(s);
s.sayHi();
</script>
</html>
10.原型继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function Person(name, age) {
this.type = 'human';
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person,
sayHi: function () {
console.log('hi');
},
};
function Studnet(name, age, score) {
Person.call(this, name, age);
this.score = score;
}
// 原型继承
Studnet.prototype = new Person();
var s = new Studnet('zhangsan', 18, 100);
console.log(s);
s.sayHi();
</script>
</html>
11.函数变量提升
函数声明:function foo () {}
函数表达式:var foo = function () {}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
console.log(a); // a 变量提升 undefined
var a = 10;
foo(); // 函数变量提升 正常
function foo() {
console.log('foo');
}
console.log(bar); // undefined
var bar = function () {
console.log('bar');
};
</script>
</html>
12.函数进阶-函数内 `this` 指向的不同场景
调用方式 | 非严格模式 | 备注 |
普通函数调用 | window | 严格模式下是undefined |
构造函数调用 | 实例对象 | 原型方法中this也是实例对象 |
对象方法调用 | 该方法所属对象 | 紧挨着的对象 |
时间绑定方法 | 绑定事件对象 | |
定时器函数 | window |
13.备份this
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
var obj = {
sayHi: function () {
console.log(this); // obj
var _this = this;
setTimeout(function () {
console.log(_this); // obj
console.log(this); // window
}, 1000);
},
};
obj.sayHi();
</script>
</html>
14.apply
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function Fruits() {}
Fruits.prototype = {
color: 'red',
say: function () {
console.log('My color is ' + this.color);
},
};
var banana = {
color: 'yellow',
};
var fruit = new Fruits();
fruit.say(); //My color is red
fruit.say.apply(banana); // My color is yellow
</script>
</html>
15.call
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function Fruits() {}
Fruits.prototype = {
color: 'red',
say: function () {
console.log('My color is ' + this.color);
},
};
var banana = {
color: 'yellow',
};
var fruit = new Fruits();
fruit.say(); //My color is red
fruit.say.call(banana); // My color is yellow
</script>
</html>
16.bind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function Fruits() {}
Fruits.prototype = {
color: 'red',
say: function () {
console.log('My color is ' + this.color);
},
};
var banana = {
color: 'yellow',
};
var fruit = new Fruits();
fruit.say(); //My color is red
fruit.say.bind(banana)(); // My color is yellow
</script>
</html>
小结:
17.函数的其它成员
arguments实参集合、length形参的个数、name函数的名称
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function foo() {
console.log(arguments); // 参数列表 通常用来获取不定参数个数的情况
console.log(length); // 0
console.log(foo.name);
}
foo(1, 2, 3, 4, 5, 6);
</script>
</html>
18.高阶函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
</body>
<script>
// 1.参数有是一个函数
function foo(callback) {
var a = Math.random();
callback(a);
}
foo(function (a) {
console.log(a);
});
var arr = [];
arr.forEach(function (item, index, arr) {
console.log(item, index);
});
// 2.返回值是一个函数
var btns = document.querySelectorAll('button');
btns.forEach(function (item, index) {
// btnClick(index) 的结果是一个函数
item.onclick = btnClick(index);
});
function btnClick(index) {
return function (event) {
console.log('点击了第' + index + '个');
};
}
</script>
</html>
19.函数闭包
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function fn() {
var count = 0;
return {
getCount: function () {
console.log(count);
},
setCount: function () {
count++;
},
};
}
var fns = fn();
fns.getCount(); // => 0
fns.setCount();
fns.getCount(); // => 1
</script>
</html>
20.作用域
全局作用域
函数作用域
没有块级作用域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width='device-width', initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
// 全局作用域
var a = 10;
console.log(a); // 10
function foo() {
// 函数作用域
var a = 20;
console.log(a); // 20
}
foo();
console.log(a); // 10
// 没有块级作用域
if (true) {
var b = 123;
}
console.log(b); // 123
</script>
</html>
21.作用域链
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
var a = 10;
function fn() {
var b = 20;
function fn1() {
var c = 30;
console.log(a + b + c); // 60
}
function fn2() {
var d = 40;
console.log(c + d); // 报错 c is not defined
}
fn1();
fn2();
}
fn();
</script>
</html>