1.对象:
1.概述:
在js中除了5中基本类型之外,剩下得都是对象Object类型(引用类型),他们的顶级父类是Object;
2.形式:
在js中,对象类型的格式为key-value形式,key表示属性,value表示属性的值
3.创建对象的方式:
方式1:通过new关键字创建(不常用)
let person = new Object();
// 添加属性 与 值
person.name="张三";
person.age = 22;
console.log(person)
方式2:通过{}类似于JSON字符串的形式创建:
let person = {
name: '张三',
age: 20
}
console.log(person);
4.对象的相关操作:
1.添加属性:
let person = new Object();
// 添加属性 与 值
person.name="张三";
person.age = 22;
console.log(person)
let person = {
name: '张三',
age: 20
}
console.log(person);
// 添加属性
person.sex = '男';
2.获取属性值:
对于new方式创建的对象,获取属性值的方式如下:
let person = new Object();
// 添加属性 与 值
person.name="张三";
person.age = 22;
console.log(person.name)
对于{}方式创建的对象,获取属性值得方式如下(上面得方式也适用):
let person = {
name: '张三',
age: 20
}
console.log(person['age']);
3.删除属性:通过delete关键字实现(两种创建方式都适用)
let person = {
name: '张三',
age: 20,
sex:'男'
}
delete person.sex;
console.log(person);
4.遍历对象属性:for-in循环
let person = {
name: '张三',
age: 20,
sex:'男'
}
for (let personKey in person) {
console.log(personKey,person[personKey]);
}
2.函数:不依赖于实例对象
1.概述:
是由一连串的子程序(语句的集合)组成的,可以被 外部调用,向函数内部传入参数之后,函数可以返回的一定的值得代码集合;
2.函数对象的创建:
方式1;通过new关键字:(不常用)
let 函数名=new Function("执行的语句");
let funA = new Function("console.log('函数对象执行了')");
//调用函数:
funA();
方式2:声明式创建函数
function 函数名(形参1,形参2.....) {
语句
}
function sum(num1, num2, num3) {
console.log("执行了sum函数:收到了参数:", num1, num2, num3)
//可以使用return 返回结果
return num1 + num2;
}
//调用函数
let ret=sum(1,2);
console.log(ret);
注意事项:
-
调用有参函数时,传入的参数不受函数定义的函数列表影响,可以多传,少穿,或者不传;
-
创建函数时,不涉及返回值(类似于Java中的构造方法),但函数体内可以return执行结果;
3.函数的类型:
1.常规函数:上述函数即为常规函数
function 函数名(形参1,形参2.....) {
语句
}
2.匿名函数:没有函数名,而是由一个变量进行接收
let 变量名(函数名)=function(形参1,形参2.....) {
执行语句
}
3.嵌套函数:即函数体内包含一个子函数
function 父函数名(形参1,形参2.....) {
function 子函数名(形参1,形参2.....) {
语句
}
}
注意:直接调用父函数时,无法执行子函数
function fu() {
function zi() {
console.log("我是子函数");
}
父函数的其他执行语句
}
//调用父函数
fu();
如果需要执行子函数,则需要在父函数中手动调用子函数
function fu() {
function zi() {
console.log("我是子函数");
}
zi();
// 父函数的其他执行语句
}
//调用父函数
fu();
4.立即执行函数:可理解为函数一创建出来就被调用执行
(function (形参1,.....) {
执行语句
})(实参.........)
(function (msg) {
console.log("我是一个匿名函数",msg)
})('我是一段消息');
3.方法:需依赖于示例对象
1.方法的定义:
需要先创建对象,然后依赖对象在创建方法;
let person = {
name:'张三',
age:23,
//定义方法
sayHello:function () {
console.log(this.name+",Hello")
}
}
console.log(person)
//调用方法
person.sayHello();
2.this关键字:
1.this出现在函数中:被直接调用,则this表示window对象
<script>
function fun() {
console.log(this.constructor+ ",Hello")
}
fun();
</script>
通过输出的构造方法名,可以看出此时的this表示window对象
2.this出现在方法中:this表示当前对象(谁调用,this就指代谁)
function fun() {
console.log(this.name + ",Hello")
}
let person = {
name: '张三',
age: 23,
sayHello: fun
}
let person2 = {
name: '李四',
age: 23,
sayHello: fun
}
person.sayHello(); //对象调用方法
person2.sayHello();
通过测试结果可以看出,在方法中的this被那个对象调用,this就指代那个对象
4.创建对象的几种方式:
1.直接创建:
存在问题:
如果需要创建的对象过多,直接创建无法实现代码的高复用性,代码冗余严重;
2.通过工厂模式,封装创建对象的方法:
实现:
function createPerson(name, age) {
let obj = new Object();
obj.name = name;
obj.age = age;
obj.sayHello = function () {
console.log(this.name)
}
return obj;
}
let person1 = createPerson('张三', 22); //Object 类型
let person2 = createPerson('李四', 22); //Object 类型
console.log(person1,person2)
存在问题:
通过下面测试结果可以看出,使用工厂模式创建的对象没有独立的类型,全部都是Object;
function createPerson(name, age) {
let obj = new Object();
obj.name = name;
obj.age = age;
obj.sayHello = function () {
console.log(this.name)
}
return obj;
}
let person1 = createPerson('张三', 22); //Object 类型
let person2 = createPerson('李四', 22); //Object 类型
console.log(typeof person1, typeof person2)
3.通过构造函数创建对象:
构造函数是什么:
-
构造函数就是一个普通的函数,创建方式和普通函数没有区别;
-
不同的是 构造函数一般首字母大写,调用时不同 需要使用new关键字;
构造函数的执行流程:
-
1.调用构造函数,会立刻创建一个新的对象
-
2.将新建的对象设置为函数中的this,在构造函数中可以使用this开引用新建的对象
-
3.逐行执行函数中的代码
-
4.将新建的对象返回
实现通过构造函数创建对象:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name)
}
}
let person1 = new Person('张三', 23);
let person2 = new Person('李四', 23);
console.log(person1, person2)
说明:
通过此种方式创建的对象,都有独立的类型,不再是object,而是与构造函数有关
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name)
}
}
let person1 = new Person('张三', 23);
let person2 = new Person('李四', 23);
console.log(person1, person2)
function Person2(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name)
}
}
let person3 = new Person2('李四', 23);
// 判断 person3 是 Person2 / Person 创建的
console.log(person3 instanceof Person)
console.log(person2 instanceof Person)
console.log(person3 instanceof Person2)
由于person3是通过Person2构造函数创建的,所以 console.log(person3 instanceof Person)输出为false,console.log(person3 instanceof Person2)输出为true;
5.原型:
1.概述:
-
我们创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象,即显式原型,
-
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
-
普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__(隐式原型)来访问该属性。
2.案例解析:
//创建构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name)
}
}
//将属性 或者 函数 存在原型中 共享的
Person.prototype.xxx = "我是测试数据";
Person.prototype.showInfo = function () {
console.log(this.name, "我是原型中的方法")
}
let person1 = new Person('张三', 23);
let person2 = new Person('李四', 23);
console.log(person1, person2);
//创建构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name)
}
}
//将属性 或者 函数 存在原型中 共享的
Person.prototype.xxx = "我是测试数据";
Person.prototype.showInfo = function () {
console.log(this.name, "我是原型中的方法")
}
let person1 = new Person('张三', 23);
let person2 = new Person('李四', 23);
//person1 访问xxx 先从person1对象自身寻找xxx属性
//没有的 就去原型中找
// 在没有 就通过父类找
console.log(person1.xxx, person2.xxx)
person2.showInfo();

因为在Person类的原型对象中添加了属性xxx和showinfo方法,而person1和person2作为它的实例对象,在访问时,由于从自身无法获取到,就向上在共享的原型对象中访问到了属性xxx和方法showinfo;
//创建构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name)
}
}
//将属性 或者 函数 存在原型中 共享的
Person.prototype.xxx = "我是测试数据";
Person.prototype.showInfo = function () {
console.log(this.name, "我是原型中的方法")
}
let person1 = new Person('张三', 23);
let person2 = new Person('李四', 23);
//使用 in 检查对象中是否 含有某个属性 有 true 没有 false
console.log('name' in person1);
console.log('xxx' in person1);
console.log(person1.hasOwnProperty('name'))
//true xxx不是person1 自身的数据
console.log(person1.hasOwnProperty('xxx'))
console.log(person1.__proto__.hasOwnProperty('xxx'))
由于xxx属性是原型对象中的属性,而不是person1自身的属性,所以测试结果为false;
6.继承:
1.方式:
-
** * 1.原型链继承**
-
** * 2.构造方法继承**
-
** * 3.组合继承**
-
** * 4.原型式继承**
-
** * 5.寄生继承**
-
** * 6.寄生组合继承**
2.原型链继承:
实现:
function SupperType() {
this.supProp = "我是父类型中的属性"
}
//给父类的原型添加方法
SupperType.prototype.showSupperProp = function () {
console.log(this.supProp);
}
//创建子类原型
function SubType() {
this.subType = "我是子类的属性"
}
//继承 让子类的原型属性 指向 父类类型
SubType.prototype = new SupperType();
//将子类的原型的构造方法属性设置为子类自己
SubType.prototype.constructor = SubType;
//子类的原型对象添加方法
SubType.prototype.showSubProp = function () {
console.log(this.subType)
}
let subType = new SubType();
//调用父类的原型中的方法
subType.showSupperProp();
//调用子类自己的原型中的方法
subType.showSubProp();
//获取父类中的属性
console.log(subType.supProp)
console.log(subType)
说明:
上述代码中是通过将子类的原型对象指向父类对象来实现的,在子类访问父类属性或方法时,先在自身找,如果找不到,再在子类的原型对象中找,要是还找不到,就在父类对象中找,父类对象属性中也没有,就在父类的原型对象中找,就这样以引用链的形式查找,进而实现了继承;
存在问题:
-
1.不能为父类传参;
-
2.原型链继承多个实例的引用类型属性,且由于指向是相同的,一个实例修改了原型属性,另一个实例的原型属性也会被影响;
3.构造函数继承:
实现:
//1.定义父类的构造函数
function SupperType(name) {
this.name = name;
this.showSupperName = function (){
console.log(this.name,"这是方法")
}
}
SupperType.prototype.xxx= "父类原型属性";
//2.创建子类构造函数
function SubType(name,age) {
//在子类中 调用call 继承父类中的属性与方法
SupperType.call(this,name);
this.age = age;
}
SubType.prototype.showInfo = function () {
console.log(this.name,this.age)
}
let subType = new SubType('张三',20);
subType.showSupperName();
//子类原型中的方法
subType.showInfo();
//通过子类实例 调用父类原型中的属性
console.log(subType.xxx);
console.log(subType)
说明:
通过此方式实现继承与原型链的方式不同,此方式更像是把父类的属性和方法复制到子类中,虽然最后看似在调用父类的属性和方法,但其实是调用本类的属性和方法,因此通过此方式并没有建立完全的继承关系,所以subType的父类是Object,而非SupperType,因此子类SubType是无法访问到父类SupperType的原型对象中的属性和方法的;
存在问题:
通过构造函数实现继承关系,解决了原型链继承的问题.但又出现了下面的新问题:
-
无法访问父类原型对象中的方法或属性;
4.组合继承:
实现:
<script>
//1.定义父类的构造函数
function SupperType(name) {
this.name = name;
this.showSupperName = function (){
console.log(this.name,"这是父类方法")
}
}
SupperType.prototype.xxx= "父类原型属性";
//2.创建子类构造函数
function SubType(name,age) {
//在子类中 调用call 继承父类中的属性与方法
SupperType.call(this,name);
this.age = age;
}
SubType.prototype=Object.create(new SupperType());
SubType.prototype.constructor=SubType;
SubType.prototype.showinfo=function(){
console.log(this.name,this.age,"子类方法")
}
let sub=new SubType("张三",22);
sub.showSupperName();
sub.showinfo();
console.log(sub.xxx);
console.log(sub);
</script>
说明:
此方式结合了引用链继承和构造方法继承的特点,解决了它们自身存在的问题
存在问题:
虽然组合继承解决了引用链继承和构造方法继承所存在的问题,但有出现了新的问题;
-
父类中的实例属性和方法在子类实例中又在子类原型中,内存开销变大;