js的面向对象

  • Post author:
  • Post category:其他




js的面向对象




一、面向对象



1.认识对象



一.认识对象

对象(object)是“键值对”的集合,表示属性和值的映射关系

属性名(键名,key) 属性值(value) 又可以叫

k:v



如果属性名不符合js标识符命名规范,则必须用方括号的写法访问

如果属性名以变量形式存储,则必须使用方括号形式

delete操作符删除某个对象的属性



二.对象的方法

如果某个属性值是函数,则它也被称为对象的方法

方法也是函数,只不过方法是对象的函数属性,打点调用即可



三.对象的深浅克隆



1.浅克隆


浅克隆

:只克隆对象的“表层”,如果对象的某些属性值又是引用类型值,则不进一步克隆它们,只是传递它们的引用,浅克隆需要使用for…in

		var obj1 = {
            a: 1,
            b: 2,
            c: [1]
        }
        var obj2 = {};
        for (var k in obj1) {
            obj2[k] = obj1[k]
        }
        obj1.a++;
        obj1.c.push(2);
        //obj1 {a: 2, b: 2, c:1,2}
        //Obj2 {a: 1, b: 2, c:1,2}
        console.log(obj1, obj2);      

以上案例,由于浅克隆只会克隆对象的第一层,后面对象值如果属于引用类型的话是不会克隆的,则会造成一种藕断丝连的现象,obj1.c和obj2.c指向的是同一个对象,改变里面的对象obj1和obj2都会跟着改变



2.深克隆


深克隆

:克隆对象的全貌,不论对象的属性值是否又是引用类型值,都能将它们实现克隆,

对象的深克隆需要使用递归

        var obj3 = {
            a: 1,
            b: 2,
            c: [33, 44, {
                m: 55,
                n: 66,
                p: [77, 88]
            }]
        }
        function deppClone(obj) {
            //判断是否为数组
            if (Array.isArray(obj)) {
                var result = [];
                for (var i = 0; i < obj.length; i++) {
                    result.push(deppClone(obj[i]))
                }
            } else if (typeof obj === "object") {
                var result = {};
                for (var k in obj) {
                    result[k] = deppClone(obj[k])
                }
            } else {
                var result = obj;
            }
            return result
        }
        var obj4 = deppClone(obj3)
        obj4.c.push(22)
        obj4.c[2].a = 5;
        console.log(obj4.c[2]); //obj4杜绝了“藕断丝连”现象
        console.log(obj4, obj3);

以上案例,深克隆把对象所有里面的值都克隆到了新的对象里面,包括了值是引用类型的,杜绝了与obj3藕断丝连的现象



2.认识函数的上下文

函数中可以使用this关键字,它表示函数的上下文

函数的上下文由调用方式决定的,同一个函数,用不同的形式调用它,函数的上下文也不一样

规则1:对象打点调用它的方法函数,则函数的上下文是这个打点的对象

        function fun() {
            console.log(this.a + this.b);
        }
        var obj1 = {
            a: 1,
            b: 2,
            fn: function() {
                console.log(this.a + this.b);
            }
        }
        var a = 5;
        var b = 4;
        var fn = obj1.fn
        fn()	//9

规则2:圆括号直接调用函数,则函数的上下文是window对象

		function fun() {
            return this.a + this.b
        }
        var a = 1;
        var b = 2;
        var obj = {
            a: 3,
            b: fun(),
            fun: fun
        };
        var result = obj.fun()
        console.log(result);	//6

规则3:数组(类数组对象)枚举出函数进行调用,上下文是这个数组(类数组对象)

        function fun() {
            arguments[3]();
        }

        fun('A', 'B', 'C', function() {
            console.log(this[1]);
        });		//B

规则4:IIFE中的丞数,上下文是window对象

        var a = 1;
        var obj = {
            a: 2,
            fun: (function() {
                var a = this.a;
                return function() {
                    console.log(a + this.a);
                }
            })()
        };
        obj.fun();	//3

规则5:定时器、延时器调用函数,上下文是window对象

        var obj = {
            a: 1,
            b: 2,
            fun: function() {
                console.log(this.a + this.b);
            }
        }
        var a = 3;
        var b = 4;
        setTimeout(obj.fun, 2000)		//7
        setTimeout(function() {
            obj.fun()		//3
        }, 2000)

规则6:事件处理函数的上下文是绑定事件的DOM元素

        function setColorToRed() {
            //备份上下文
            var self = this;
            setTimeout(function() {
                self.style.backgroundColor = "red"
            }, 2000)
        }
        var box = document.querySelector('.box');
        box.addEventListener("click", setColorToRed)

call和apply任意指定this指向

		//call和apply能指定函数的上下文
        function sum(target, target1) {
            console.log(this.a + this.b + target + target1)
        }

        var obj = {
            a: 1,
            b: 3,
        };
        // call要用逗号罗列参数
        sum.call(obj, 4, 5);
        // apply要把参数写到数组中
        sum.apply(obj, [4, 6])

new调用函数

        function fun() {
            this.a = 3;
            this.b = 5;
        }
        var obj = new fun();
        console.log(obj);  



3.构造函数

1.用new调用一个函数,这个函数就被称为“构造函数”,任何函数都可以是构造函数,只需要用new调用它

2.构造函数用来”构造新对象”,它内部的语句将为新对象添加若干属性和方法,完成对象的初始化

3.构造函数必须使用new关键字调用,构造函数命名时首字母要大写

构造函数可以类比与类,由类创造实例的过程称为实例化



4.原型和原型链

任何函数都有prototype属性,prototype是英语“原型”的意思

prototype属性值是个对象,它默认拥有constructor属性指回函数

普通函数来说的prototype属性没有任何用处,而构造函数的prototype属性非常有用

构造函数的prototype属性是它实例的原型

在这里插入图片描述

        function People(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        //实例化
        var xiaoming = new People('小明', 12, "男");
        //测试三角关系是否存在
        console.log(xiaoming.__proto__ === People.prototype);  //true

People是一个构造函数,People的prototype属性指向的是People.prototype原型,用new创建了一个obj实例,obj实例的原型__proto__指向People.prototype,构造函数创建obj实例的过程叫实例化

原型链查找:实例可以打点访问它的原型的属性和方法

//hasOwnProperty方法可以检查对象是否真正“自己拥有某属性或者方法”
        function People(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        //实例化
        var xiaoming = new People('小明', 12, "男");
        xiaoming.hasOwnProperty("sex") //true
//in运算符只能检查某个属性或方法是否可以被对象访问,不能检查是否是自己的属性或方法
		"name" in xiaoming //true

把方法直接添加到实例身上的缺点:每个实例和每个实例的方法函数都是内存中不同的函数,造成了内存的浪费,将方法写在prototype上可以解决

        function People(name, sex, age) {
            this.name = name;
            this.sex = sex;
            this.age = age;
            this.aa = function() {
                console.log(1);
            }
        }
        //把方法写在原型上
        People.prototype.sayHello = function() {
            console.log(this.name + "今年" + this.age + "岁了");
        }
        People.prototype.growup = function() {
            this.age++;
        }
        var xiaoming = new People("小明", "男", 18);
        var xiaohong = new People("小红", "女", 20);

        console.log(xiaoming.sayHello === xiaohong.sayHello);
        console.log(xiaoming.aa === xiaohong.aa);

        xiaoming.growup()
        xiaoming.sayHello()
        xiaohong.sayHello()


原型链终点


在这里插入图片描述

        function People(name, age) {
            this.name = name;
            this.age = age;
        }

        var obj = new People();

        console.log(obj.__proto__ === People.prototype);		//true
        console.log(obj.__proto__.__proto__ === Object.prototype);		//true



5.继承

继承描述了两个类之间的一种关系,分为父类和子类,子类丰富了父类,让类描述的更加具体细化

实现继承的关键在于:子类必须拥有父类的全部属性和方法,同时子类还应该能定义自己特有的属性和方法,使用js特有的原型链特性来实现继承,是很常用的方法

        function People(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }

        People.prototype.sayHello = function() {
            console.log("我叫" + this.name + "今年" + this.age + "岁了");
        }
        People.prototype.sleep = function() {
            console.log(this.name + "开始睡觉,zzzzz");
        }

        function student(name, age, sex, school, studentId) {
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.school = school;
            this.studentId = studentId;
        }
        //实现继承
        student.prototype = new People()

        student.prototype.study = function() {
            console.log(this.name + "正在学习");
        };
        //重写(override)父类的sayHello
        student.prototype.sayHello = function() {
            console.log("大家好,我叫" + this.name + "今年" + this.age + "岁了");
        }

        var xiaoming = new student("小明", 15, "男", "中心校", 14238)

        console.log(xiaoming.__proto__ === student.prototype);		//true
        console.log(xiaoming.__proto__.__proto__ === People.prototype);		//true

        xiaoming.sayHello()		//大家好,我叫小明今年15岁了
        xiaoming.study()		//小明正在学习

        var xiaohong = new People("小红", 14, "女");
        xiaohong.sayHello()	//我叫小红今年14岁了
        xiaohong.study()		//xioahong is not defined



版权声明:本文为qq_56402822原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。