一、概念
面向对象:对于软件开发模式有两种,一种是面向对象,一种是面向过程。面向过程:只完成自己所需要的操作,但是这种设计缺少可维护性。面向对象:本质上是组件化的设计(模块化设计),方便局部维护但是设计上的要求规范比较多,也就是模块化的设计最重要的就是标准,以及整个项目的整体把控。
面向对象的概念实际上有以下几个特点:封装性,保护内部的操作对外不可见;继承性:相当于一代代的传承问题;多态性:在一个范围内的定义改变。js可以模拟实现继承和封装,但不能模拟实现多态,所以js是基于事件,基于对象的语言。
面向对象编程(Object Oriented Programming)
简称"OOP",是一种编程开发思想,它将真实世界的各种复杂关系抽象成一个个对象,然后由对象之间的分工合作完成对现实世界的模拟。
类和对象的概念:
类:具有相同特征(属性)和行为(方法)的集合。如:人类->:属性:姓名、三围、星座 方法-> 吃、喝
对象:从类中拿出具有确定属性值和方法的个体叫做对象。可以这样说,对象是包含属性和方法的集合,万物皆对象。
二者关系:类是抽象的,对象是具体的,类是对象的抽象化,对象是类的具体化。
二、创建对象
1. 用字面量创建,不利于复用代码。
<script>
var person = {
name: ‘davina‘,
age: 20,
play: function () {
console.log(‘write code‘);
}
}
console.log(person); //{name: "davina", age: 20, play: ƒ}
person.heigth = ‘170CM‘;
console.log(person); //{name: "davina", age: 20, heigth: "170CM", play: ƒ}
</script>
可以另外添加属性和方法,添加属性即为描述对象的特征,属性的值为非函数的任意数据类型,添加的方法是实现对象的一些功能,方法的值为函数。
2、构造函数
在js中,构造函数就是一个用来生成对象的函数,所有的对象都是由它创建。构造函数首字母一般大写,它与普通函数的区别是是否由new操作符调用 。new是语法糖,使用new操作符调用构造函数时,会创建一个新对象,将this指向该对象(将构造函数的作用域赋给新对象)。
在普通函数执行的基础上加了“new xxx()”,这样的话就不是普通执行了,而是构造函数执行,当前的函数名称为“类名”,接收的返回结果是当前类的一个实例,实例由构造函数生成,平时用的都是实例化对象。简单来说,通过构造函数new出来的的对象叫做实例,创建对象的过程叫做实例化。实例化对象身上有一个constructor指向对应的构造函数,判断一个对象是否是某个对象的实例要用到instanceof运算符。
function Fn(){
//......
}
let f = new Fn();//Fn是类,f是类的一个实例
console.log(f);
let f2 = new Fn();//f2也是Fn的一个实例,f2和f是独立分开的
基本数据类型的值基于构造函数创建出来的值和基于字面量创建出来的值是不一样。基于字面量方式创建出来的值是基本类型值,基于构造函数创建出来的值是引用类型。
//num2是数字类的实例,num1也是数字类的实例,它只是js表达数字的特殊方式之一,都是可以使用数字类提供的属性和方法
var num1 = 12;
var num2 = new Number(12);
console.log(typeof num1);//‘number‘
console.log(typeof num2);//‘object‘
console.log(num1.toFixed(2)); //12.00
console.log(num2.toFixed(2)); //12.00
普通函数执行分为五步:1、形成一个私有作用域2、形参赋值3、变量提升4、代码执行5、栈内存释放问题。构造函数的执行比普通函数执行多了两步:1、形成一个私有作用域2、形参赋值3、变量提升4、在js代码执行前改变this指向5、代码执行6、返回创建的this
function Fn(name, age) {
let n = 10;
this.name = name;
this.age = age + 10;
this.play = function(){
console.log(‘write code‘);
}
}
let f = new Fn(‘davina‘, 20);
let f1 = new Fn(‘xxx‘, 20);
let f2 = new Fn(‘aaa‘, 20);
console.log(f1 === f2);//false 两个不同的实例(两个不同学的堆内存)
console.log(f1.age, f2.name);//30 "aaa"
console.log(‘name‘ in f1);//true name&age在两个不同的实例都有存储,但是都是每个实例自己的私有的属性
console.log(f1.n);//undefined 只有this.xxx=xxx的才和实例有关,n是私有作用域中的一个私有变量而已(this是当前类的实例)
console.log(f1.constructor === Fn); //true 检查f1对应的构造函数
console.log(f2 instanceof Fn); //true 判断f1是否是Fn身上的实例
f1.play(); //‘write code‘
构造函数执行,不写return,浏览器会默认返回创建的实例,但是如果我们自己写了return分为两种情况:
1.return的是一个基本值,返回的结果依然是类的实例,没有受到影响
2.如果返回的是引用值,则会把默认返回的实例覆盖,此时接收到的结果就不在是当前类的实例了。所以构造函数执行的时候 ,尽量减少return的使用,防止覆盖实例。
用new调用的函数,这个函数就是一个用来创建对象的函数即构造函数,它得到的结果永远是一个对象,不管函数有无返回值。
function Sum() {
let n = 10;
this.m = n;
return;//这样的return是结束代码执行的作用,且不会覆盖实例
return { name: ‘davina‘ };
}
var f3 = new Sum();//new Sum;//在构造函数执行的时候 ,如果Fn不需要传入实参,我们可以省略小括号
console.log(f3); //{name: "davina"}
继承:一个对象身上有另一个对象身上的属性或方法,这种具有的方式就叫做继承,生成的实例具有构造函数身上的属性和方法。(不光是实例可以继承构造函数,对象与对象也是可以继承的)。
三、原型及原型链
prototype(原型):只有函数才有prototype且所有的函数都是有prototype。它的类型为对象,指向了当前函数所在的引用地址。它内部存储着constructor构造器,这个对象里放的属性与方法就是构造函数的共享属性与方法,所有实例都能用。prototype原型对象中的constructor属性指向的是prototype自己所在的构造函数。
__proto__(原型) :在实例化对象中存在着__proto__的属性,它的名字也是叫做原型,它指向构造函数中的prototype对象所在的内存空间,__proto__==prototype。注意:__proto__它是对象身上的一个属性而prototype是函数身上的属性。
总结:只有对象才有__proto__且所有的对象必然有__proto__.数组也是对象,也有自己的__proto__;__proto__也是一个对象,所以也有自己的__proto__,顺着这条线向上找就是原型链。
构造函数身上的属性与方法,只有构造函数能用,实例不能用,实例身上的属性与方法只有实例可用,构造函数不能用(放在prototype中了)。
原型链:是对象与原型之间的链接(关系)。查找规则如下:
当我们调用一个对象身上的属性或者方法时,它就会有一个查找规则。首先在自己身上去找,如果有就用自己的,如果没有的话,它就在这个对象的__proto__下查找,因为__proto__这个属性是指向对应的构造函数身上的prototype,找它的时候就是找构造函数的原型。如果原型身上也没有,那它继续向外找,直到找到最顶层Object身上的prototype为止。
分析如下:
<script>
console.log(Array);//构造函数 ƒ Array() { [native code] }
var arr = [1, 2, 3];
console.log(arr instanceof Array); //true
//构造函数的私有属性与方法
console.log(Array.name); //Array
console.log(Array.of(5)); //[5]
console.log(arr.name) //undefined
//console.log(arr.of(5)); //TypeError: arr.of is not a function
//共享属性与方法
console.log(arr.concat([‘a‘, ‘b‘])); //(5) [1, 2, 3, "a", "b"]
//console.log(Array.concat([‘a‘,‘b‘])); //报错,实例身上的属性于方法,构造函数不能直接用
console.log(Array.prototype.concat([‘a‘, ‘b‘])); //(2) ["a", "b"]
//prototype
Array.prototype.push = null; //prototype是可以修改的,但是不要修改
//__prototype__
const str = new String(‘davina‘); //实例 String是一个构造函数
console.log(String.prototype); //String {"", constructor: ƒ, anchor: ƒ, big: ƒ, blink: ƒ, …} 通过构造函数找到构造函数身上的原型
console.log(str.constructor === String); //true
console.log(str.constructor.prototype === String.prototype); //true
console.log(__proto__ === constructor.prototype); //true
console.log(str.__proto__ === String.prototype); //true 实例对象的一个__prototype指向构造函数的prototype
// 构造函数是函数,函数是对象,所以构造函数上也有__proto__属性
//console.log(String.__proto__===函数的构造函数的prototype)
console.log(String.__proto__ === Function.prototype); //true
console.log(String.__proto__.__proto__ === Object.prototype) //true
</script>
范例:
function Fn() {
var n = 100;
this.AA = function () {
console.log(‘aa私有‘);
}
this.BB = function () {
console.log(‘bb私有‘);
}
}
Fn.prototype.AA = function () {
console.log(‘AA公有‘);
}
let f1 = new Fn;
let f2 = new Fn;
console.log(f1.n);//undefined
console.log(f1.__proto__.AA === f2.__proto__.AA);//true
console.log(__proto__.AA === Fn.prototype);//false
console.log(f1.hasOwnProperty === Fn.prototype.hasOwnProperty);//true
深入理解构造函数的私有属性和方法及对象的共有属性与方法
<script>
function Person() {
Person.name = ‘davina‘;
Person.play = function () {
console.log(‘write code‘)
}
Person.prototype.say = function () {
console.log(‘人可生如蚁而美如神!‘)
}
}
//实例
const person1 = new Person();
console.log(person1.name); //undefined
//person1.play(); //TypeError: person1.play is not a function
person1.say(); //人可生如蚁而美如神!
console.log(person1.__proto__ === Person.prototype); //true
console.log(person1.constructor.prototype === Person.prototype) //true
</script>
四、es6中新增class关键字创建类
用class关键字声明一个类,首字母大写
语法规范:
类里面的constructor函数,存放的是类的共有属性,可以接受传递过来的参数,同时返回实例对象,只要new生成实例时,就会自动的调用这个函数,如果不写这个函数,类会自动生成这个函数;生成实例的new不能省略;创建类时,类名后面不加小括号,生成实例时,类后面加小括号;构造函数里不需要加function(类里面的所有函数都不需要添加function);多个函数方法之间不需要用逗号分隔。
<!-- <script>
class name[extends]{
//calss body
}
extends为继承,是个可选的参数
</script> -->
强调的是:声明的类还是一个构造函数,共享方法直接写,它会自动的放在prototype上,共享属性需要写在constructor里
<script>
//es6的写法 创建一个Person类
class Person {
//类的共有属性放到constructor中
constructor(name, age) {
this.name = name;
this.age = age; //共享属性
}
say() { //共享方法 自动放在prototype
console.log(‘人可生如蚁而美如神!‘)
}
}
//利用类创建对象
const [person1, person2] = [new Person(‘davina‘, 20), new Person(‘lisa‘, 21)]; //这是两个实例
person1.sex = ‘male‘;
console.log(
typeof Person, //function 构造函数,虽说是类,但本质上还是构造函数
Person.prototype.constructor === Person, // true
person1.__proto__ === Person.prototype, // true
person1 instanceof Person, //true
person2.constructor == Person, ///true
Object.getOwnPropertyNames(person1),// ["name", "age", "sex"] 找实例身上自己的属性
person1.hasOwnProperty(‘say‘), //false say是放在prototype里,继承而来,并不是实例身上自有的
Object.keys(Person.prototype), // [] 内置身上的方法一般是不可以被枚举
)
// class Person{} //SyntaxError: Identifier ‘Person‘ has already been declared 不能重复的声明
//构造函数和实例放在一起
const MyPerson = new class {
move() {
console.log(12);
}
}
MyPerson.move(); //12
</script>
原文:https://www.cnblogs.com/davina123/p/12023907.html