面向对象
面向对象
1.什么是面向对象
面向对象就是:把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其共性,形成类。类中的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。程序流程由用户在使用中决定。对象即为人对各种具体物体抽象后的一个概念,人们每天都要接触各种各样的对象,如手机就是一个对象。
2.面向对象
- 在js中,有2种编程思维
- 面向过程:
- 面向对象:
- 核心就是对象(类),对象的组成有2种 {“name”:ls, skill:function(){console.log(“敲代码”)} }
- 属性:描述 静态的
- 方法:行为 动态的
- 面向对象三大特点
- 封装
- 继承
- 多态
2.1创建面向对象
2.1.1字面量创建
缺点:创建单个
var obj = {
"name": "zs",
"age": 23,
"skill": function () {
console.log("敲代码")
}
}
console.log(obj);
console.log(obj.age);//23
obj.skill(); //敲代码
</script>
2.1.2new关键字创建
缺点:代码冗余
<script>
// 创建张三的对象
// 1.创建空对象
var obj = new Object();
console.log(obj);//{}
// 2.添加属性和方法
obj.name = "李四";
obj.age = 20;
obj.skill = function(){
console.log("敲代码")
}
console.log(obj);
obj.skill();
</script>
2.1.3工厂模式创建
缺点:类别识别不明确,代码冗余 浪费内存
<script>
function createObj(name,age){ // name 张三 age 20
//1.创建空对象
var obj = new Object();
//2.添加属性和方法
obj.name = name;
obj.age = age;
obj.skill = function(){
console.log("敲代码")
}
// 3.出厂 设置返回值
return obj;
}
var res1 = createObj("张三",20);
console.log(res1);
var res2 = createObj("李四",20);
console.log(res2);
</script>
2.1.4构造函数创建
缺点:浪费内存
如何创建构造函数
- 构造函数的函数名首字母必须大写,为了和普通函数做区别
- 方法和属性是直接给this
- 必须使用new关键字进行调用 否则和普通函数没有区别
new操作符做了什么
- 隐式创建了一个空对象,让this指向这个空对象
- 执行构造函数中的代码
- 将实例化对象的__ proto __ 指向构造函数的prototype
- 隐式返回对象
<script>
// 创建构造函数
function Person(name, age) {
//new关键字 隐式创建了一个空对象 让this指向这个空对象
//添加属性和方法 必须加给this
this.name = name,
this.age = age,
this.skill = function () {
console.log("敲代码")
}
// new关键字 隐式返回这个对象
}
// 实例化对象(调用构造函数) 必须使用new关键字
var res1 = new Person("张三", 20);
console.log(res1);
var res2 = new Person("李四",10);
</script>
2.1.5原型创建
缺点:不能传参
//创造构造函数
function Person(){}
// 将方法和属性添加到构造函数的prototype上 共享的属性和方法
console.dir(Person);
Person.prototype.name = "张三";
Person.prototype.age = 20;
Person.prototype.skill = function(){
console.log("敲代码")
}
console.dir(Person);
// 实例化对象
var res1 = new Person();
console.log(res1);//实例化对象的__proto__指向构造函数的prototype
console.log(res1.__proto__);
var res2 = new Person();
console.log(res2);
console.log(res2.__proto__);
2.1.6混合创建
构造函数创建(传参)+原型创建(不传参)
<script>
/* name age传参 skill不传参 */
// 创建Student构造函数
function Student(name, age) {
this.name = name
this.age = age
}
// 原型创建 prototype
Student.prototype.skill = function () {
console.log("学习敲代码")
}
// 实例化对象
var res1 = new Student("张三", 20);
console.log(res1);
var res2 = new Student("李四", 15);
console.log(res2);
console.log(res1.name);//张三
res.skill();//学习敲代码
res.skill1();//
</script>
2.2什么是原型和原型链
- 原型prototype:构造函数中用来存储共享的方法和属性的对象
- 原型属性 __ proto __ :让实例化对象的__ proto__指向构造函数的prototype
// Array是js内部创建好的构造函数
console.dir(Array);
// 实例化对象 实例化对象的__proto__指向构造函数的prototype
var arr = new Array(1,2,3,4,5);
console.log(arr);
console.log(Array.prototype === arr.__proto__);//true
console.dir(Array.prototype);//Array上共享的属性和方法
var arr = new Array(1,2,3,4,5); //arr的__proto__ 指向Array的prototype
var arr1 = new Array(4,5,6,6);// arr1的__proto__指向Array的prototype
-
原型链:在创建构造函数和实例化对象的时候 自动形成一种查找关系
先查找自身的属性和方法 然后再找__ proto__,再找构造函数 如果都找不到返回undefined
2.3this的指向
1.this的指向问题
- 在事件处理函数中 this指向当前触发事件的对象(标签)
- 在Object数据类型中的方法中 this指向所在方法的对象
- 在普通函数中 this指向window
- 在构造函数中 this指向实例化对象
<script>
// 1.在事件处理函数中 this指向当前触发事件的对象(标签)
var btn1 = document.getElementById("btn1");
btn1.onclick = function(){
console.log(this);//
}
// 2.在Object数据类型中的方法中 this指向所在方法的对象
var obj = {
"name":"zs",
"eat":function(){
console.log(this);//obj
}
}
obj.eat();
// 3.在普通函数中 this指向window
function fun1(){
console.log(this);//window
}
fun1();
// 4.在构造函数中 this指向实例化对象
function Person(name){
this.name = name,
this.skill = function(){
console.log(this);//实例化对象
}
}
var res = new Person("ls");
res.skill();
</script>
2.如何改变this指向
- call(this的新指向,参数1,参数2…) 参数是分开写的 用逗号分隔
- apply(this的新指向,[参数1,参数2…])
var obj = {
"name": "小红",
"age": 10,
"getName": function () {
console.log(this.name);
}
}
obj.getName();// 小红 this指向 obj
var obj1 = {
"name": "小明"
}
obj.getName.call(obj1);//this指向obj1 小明
function fun1() {
console.log(this)
}
fun1();//this指向window
fun1.call(document.documentElement);
fun1.call(1);//
fun1.call("123");//
function fun2(a, b) {
console.log(a, b, this);
}
fun2(100, 200);// 100 200 window
fun2.call(obj1, 100, 200);// 100 200 obj1:{name:"小明"}
// 详细检测数据类型
console.log(Object.prototype.toString.call("124"))
console.log(Object.prototype.toString.call(123))
console.log(Object.prototype.toString.call(true))
console.log(Object.prototype.toString.call(undefined))
console.log(Object.prototype.toString.call(null))
console.log(Object.prototype.toString.call({}))
console.log(Object.prototype.toString.call([]))
console.log(Object.prototype.toString.call(function () { }))
console.log(Object.prototype.toString.call(new Date()))
console.log(Object.prototype.toString.call(/\d/gi))
//instanceof 检测当前数据是否属于某个构造函数
function Person() {
this.name = "ls"
}
var res = new Person();
// 检测res是否由Person构造函数创建的
console.log(res instanceof Person);//true
// apply(this的新指向,[参数1,参数2......])
function fun3(a,b){
console.log(this,a,b)
}
fun3();//window
fun3.apply(obj1,[100,200]);// obj1 100 200
//利用Math.max和Math.min求数组中最大的值和最小的值
var arr = [100,80,120,60];
// Math.max(序列1,序列2.....)
var a = Math.max.apply("12344",arr);
console.log(a);
var a = Math.min.apply("123",arr);
console.log(a);
3.面向对象的选项卡
<script>
// var btn = document.getElementsByTagName("button");
// var odiv = document.getElementsByTagName("div");
// for(var i = 0;i<btn.length;i++){
// btn[i].index =i;
// btn[i].onclick = function(){
// for(var j = 0;j<odiv.length;j++){
// odiv[j].style.display = "none";
// }
// odiv[this.index].style.display = "block";
// }
// }
/*
面向对象的选项卡
创建面向对象构造函数 面向对象都是由属性和方法组成的
标签作为对象的属性 点击事件和for循环作为对象的方法
*/
function SwitchTab() {
// 添加属性
this.btn = document.getElementsByTagName("button");
this.odiv = document.getElementsByTagName("div");
var that = this;// 在这里this还是指向实例化对象的
// 入口方法 添加方法
this.init = function () {
for (var i = 0; i < this.btn.length; i++) {
this.btn[i].index = i;
this.btn[i].onclick = function () {
// console.log(this);//在事件处理函数中 this指向当前触发事件的对象
// 如何让this指向实例化对象
that.changeItem(this.index);//想让this指向实例化对象
}
}
}
// 入口方法调用
this.init();
this.changeItem = function (index) {
for (var j = 0; j < this.odiv.length; j++) {
this.odiv[j].style.display = "none";
}
// this.index 想让this指向触发对象 但是现在这个this指向实例化对象
this.odiv[index].style.display = "block";
}
}
// 实例化对象
new SwitchTab();
</script>
4.面向对象的继承
4.1原型继承
缺点:不能传参 引用数据类型会一改全改
<script>
/*
创建一个大学生类
name age sex id skill必杀:谈恋爱
创建一个小学类
name age sex id skill必杀:找妈妈
*/
//父类构造函数
function Student(name, age, sex, id) {
this.name = name
this.age = age
this.sex = sex
this.id = id
this.arr = [1,2,3,4]
}
// 子类构造函数
function CollegeStudent() {
this.skill = function () {
console.log("谈恋爱")
}
}
//子类构造函数
function SmallStudent() {
this.skill = function () {
console.log("找妈妈")
}
}
// 设置继承 子构造函数的prototype = 父类构造函数的实例化对象
CollegeStudent.prototype = new Student("张三",20,"男",1);
// 实例化子类对象 大学生1
var res1 = new CollegeStudent();
console.log(res1);
console.log(res1.name);//"张三"
console.log(res1.name1);// undefined
console.log(res1.arr);//["1","2","3","4"]
res1.arr[3] = "哈哈";
console.log(res1.arr);//["1","2","3","哈哈"]
// 大学生2
var res2 = new CollegeStudent();
console.log(res2);
console.log(res2.arr);//["1","2","3","哈哈"]
// 缺点:不能传参 引用数据类型会一改全改
</script>
4.2对象冒充继承
优点:能解决传参问题 一改全改
缺点:父类上的prototype上的属性和方法不能继承
<script>
// 父类
function Student(name,age,sex,id){
this.name = name;// this指向子类
this.age = age;
this.sex = sex;
this.id = id;
this.arr = [1,2,3,4];
}
Student.prototype.classId = "0711web";
Student.prototype.eat = function(){console.log("都爱吃食堂饭")}
//子类
function CollegeStudent(name,age,sex,id){
//子类对象.name = name
// 调用父类 对象冒充继承
Student.call(this,name,age,sex,id);//this指向子类
this.skill = function(){
console.log("谈恋爱")
}
}
/*
优点:能解决传参问题 一改全改
缺点:父类上的prototype上的属性和方法不能继承
*/
// 实例化对象
var res = new CollegeStudent("张三",20,"男",1);
console.log(res);
console.log(res.classId);
</script>
4.3组合继承
- 组合继承:原型继承+对象冒充继承
<script>
/*
组合继承:原型继承+对象冒充继承
*/
//父类构造函数
function Student(name, age, sex) {
this.name = name;
this.age = age
this.sex = sex;
this.arr = [1,2,3,4];
}
Student.prototype.classid = "0711web";
Student.prototype.eat = function () {
console.log("爱吃食堂的饭")
}
//子类构造函数
function SmallStudent(name,age,sex){
// 对象冒充继承
Student.call(this,name,age,sex);
this.skill = function(){
console.log("找妈妈")
}
}
// 原型继承 子类的prototype = 父类的实例化对象
SmallStudent.prototype = new Student("如花",18,"女")
// 实例化子类对象
var res = new SmallStudent("张三",10,"男");
console.log(res);
console.log(res.name);//张三
res.eat();//爱吃食堂的饭
</script>
4.4寄生式组合继承
- 原型继承 + 对象冒充继承
<script>
/*
组合继承:原型继承+对象冒充继承
*/
//父类构造函数
function Student(name, age, sex) {
this.name = name;
this.age = age
this.sex = sex;
this.arr = [1,2,3,4];
}
Student.prototype.classid = "0711web";
Student.prototype.eat = function () {
console.log("爱吃食堂的饭")
}
//子类构造函数
function SmallStudent(name,age,sex){
// 对象冒充继承
Student.call(this,name,age,sex);
this.skill = function(){
console.log("找妈妈")
}
}
// 原型继承 子类的prototype = Object.create(原型对象) 复制父类的原型对象
SmallStudent.prototype = Object.create(Student.prototype);
SmallStudent.prototype.constructor = SmallStudent;
// 实例化子类对象
var res = new SmallStudent("张三",10,"男");
console.log(res);
console.log(res.name);//张三
res.eat();//爱吃食堂的饭
</script>