01 课程介绍
02 复习
面向过程和面向对象都是编程的思想,方式不一样
- 面向过程:凡事都是亲力亲为,所有的代码都要自己写,每一步都要很清楚,注重的是过程
- 面向对象:执行者成为指挥者,只要找对象,然后让对象做相关的事情,注重的是结果
面向对象的特性:封装,继承,多态
封装;就是代码的封装,把一些特征和行为封装在对象中.
面向对象的编程思想:根据需求,抽象出相关的对象,总结对象的特征和行为,把特征变成属性,行为变成方法,然后定义(js)构造函数,实例化对象,通过对象调用属性和方法,完成相应的需求.—编程的思想
对象:具体特指的某个事物,有特征(属性)和行为(方法)
如何创建对象?
- 通过调用new Object(),还有{},自定义构造函数
创建对象的方式
**1. 调用系统Object()----->创建出来的对象都是Object类型的,不能很明确的指出这个对象是属于什么类型
- 字面量的方式{}----->只能创建一个对象(一次只能创建一个)**
3.工厂模式创建对象----->----->推论---->自定义构造函数的方式
自定义构造函数(优化后的工厂模式)
自定义构造函数创建对象:4件事
1.在内存中申请一块空闲的空间,存储创建的对象
2.this就是当前实例化的对象
3.设置对象中的属性和方法(为对象添加属性和方法,为属性和方法赋值)
4.把创建后的对象返回
都是需要通过new的方式
什么是原型?
- 构造函数中有一个属性prototype,是原型,程序员使用的
- 实例对象中有一个属性__proto__,是原型,浏览器使用的,不是很标准的,
- 实例对象中的__proto__指向的就是该实例对象中的构造函数中的prototyp
- 构造函数中的prototype里面的属性或者方法,可以直接通过实例对象调用
正常的写法:
实例对象.__proto__才能访问到构造函数中的prototype中的属性或者方法
per.__proto__.eat();//__proto__不是标准的属性
per.eat();
原型就是属性,而这个属性也是一个对象
Person.prototype--->是属性
Person.prototype.属性或者Person.ptototype.方法()
本身在构造函数中定义的属性和方法,当实例化对象的时候,实例对象中的属性和方法都是在自己的空间中存在的,如果是多个对象。这些属性和方法都会在单独的空间中存在,浪费内存空间,所以,为了数据共享,把想要节省空间的属性或者方法写在原型对象中,达到了数据共享,实现了节点内存空间
function Person(name){
this.name=name;
}
Person.prototype.sex="男";
var per=new Person("小明");
per.sex
var per2=new Person("小芳");
per2.sex
原型的作用之一:数据共享,节省内存空间
原型的写法:
- 构造函数.prototype.属性=值
- 构造函数.prototype.方法=值---->函数.prototype,函数也是对象,所以,里面也有__proto__
- 实例对象.prototype-------->实例对象中没有这个属性,只有__proto__(暂时的)
简单的原型的写法
缺陷:—>新的知识点---->原型直接指向{}---->就是一个对象,没有构造器
构造函数.prototype={
切记:如果这这种写法,要把构造器加上
};
通过原型为内置对象添加原型的属性或者方法----->原因:
系统的内置对象的属性和方法可能不满足现在需求,所以,可以通过原型的方式加入属性或者方法,为了方便开发
为内置对象的原型中添加属性和方法,那么这个内置对象的实例对象就可以直接使用了
- String.prototype.方法=匿名函数;
- var str=“哈哈”;
- str.方法();---->实例对象可以直接调用原型中的属性或者方法
String.prototype.fdsfdsf=function(){};
凡是string的实例对象都可以调用刚刚加入的方法
案例分析:
面向对象的思想来做: 分析对象,抽象出对象的特征和行为,定义构造函数,属性可以不共享
部分方法需要共享,方法加到prototype中定义(在原型中定义方法,数据共享,节省内存空间)
食物对象(食物的横纵坐标,宽和高,背景颜色)
食物需要画出来—渲染出来–画,随机的画,在画食物的时候要先删除原来的食物
小蛇对象(宽,高,方向)
蛇需要画出来—渲染出来–画,每走一次,需要把前一次的小蛇删除
蛇走的时候,需要方向,是否吃到了食物
小蛇移动的时候,是否吃了食物(吃了就要把小蛇的后面加一个食物的宽和高,颜色,无非就是把原来的蛇尾复制了一个加入到body中,------>把蛇尾拿出来再次加入到蛇尾的后面)
游戏对象(初始化食物,初始化小蛇,自动移动小蛇,判断按键)
自动的设置小蛇移动,判断小蛇是否撞墙,用户按下了什么方向键
window.变量=值;把这个局部变量的值暴露给window,成为了全局变量
function Person(age) {
this.age=age;
this.study=function () {
}
}
Person.prototype.sex="男";//属性,属性在原型中
Person.prototype.sayHi=function () {//方法,方法在原型中
console.log("您好");
};
//实例化对象同时进行初始化
var per=new Person(10);
// console.log(per.__proto__.sex);
// per.__proto__.sayHi();
console.log(per.sex);
per.sayHi();
Person.prototype={
//简单的原型的写法,缺少构造器
constructor:Person
};
//为内置对象添加原型方法
String.prototype.sayHi=function () {
console.log("字符串的打招呼的方法");
};
//是一个实例对象
var str="字符串";
str.sayHi();
//实例对象调用属性或者方法,属性或者方法肯定是在构造函数中或者是构造函数的原型中
03 原型及原型链
使用对象---->使用对象中的属性和对象中的方法,使用对象就要先有构造函数
构造函数
function Person(name,age) {
//属性
this.name=name;
this.age=age;
//在构造函数中的方法
this.eat=function () {
console.log("吃好吃的");
};
}
添加共享的属性
Person.prototype.sex="男";
添加共享的方法
Person.prototype.sayHi=function () {
console.log("您好啊,怎么这么帅,就是这么帅");
};
实例化对象,并初始化
var per=new Person("小明",20);
per.sayHi();
如果想要使用一些属性和方法,并且属性的值在每个对象中都是一样的,方法在每个对象中的操作也都是一样,那么,为了共享数据,节省内存空间,是可以把属性和方法通过原型的方式进行赋值
console.dir(per);//实例对象的结构
console.dir(Person);//构造函数的结构
实例对象的原型__proto__和构造函数的原型prototype指向是相同的
实例对象中的__proto__原型指向的是构造函数中的原型prototype
console.log(per.__proto__==Person.prototype);
实例对象中__proto__是原型,浏览器使用的
构造函数中的prototype是原型,程序员使用的
原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(proto)来联系的
04 原型的指向是否可以改变
构造函数中的this就是实例对象
原型对象中方法中的this就是实例对象
function Person(age) {
this.age=age;
console.log(this);
}
Person.prototype.eat=function () {
console.log(this);
console.log("您吃了没,走着,吃点臭豆腐去");
};
var per=new Person(10);
per.eat();
console.log(per);
function Student() {
}
Student.prototype.study=function () {
console.log("就是天天学习,学习如何做人,如何敲代码,如何成为人");
};
Student.prototype={
eat:function () {
console.log("哈哈,好吃的榴莲酥");
}
};
var stu=new Student();
stu.eat();
//人的构造函数
function Person(age) {
this.age=10;
}
//人的原型对象方法
Person.prototype.eat=function () {
console.log("人的吃");
};
//学生的构造函数
function Student() {
}
Student.prototype.sayHi=function () {
console.log("嗨,小苏你好帅哦");
};
//学生的原型,指向了一个人的实例对象
Student.prototype=new Person(10);
var stu=new Student();
stu.eat();
stu.sayHi();
原型指向可以改变
- 实例对象的原型__proto__指向的是该对象所在的构造函数的原型对象
- 构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(proto)指向也会发生改变
原型的指向是可以改变的
- 实例对象和原型对象之间的关系是通过__proto__原型来联系起来的,这个关系就是原型链
05 原型最终指向了哪里
function Person() {
}
Person.prototype.eat=function () {
console.log("吃东西");
};
var per=new Person();
console.dir(per);
console.dir(Person);
实例对象中有__proto__原型
构造函数中有prototype原型
prototype是对象
所以,prototype这个对象中也有__proto__,那么指向了哪里
实例对象中的__proto__指向的是构造函数的prototype
所以,prototype这个对象中__proto__指向的应该是某个构造函数的原型prototype
Person的prototype中的__proto__的指向
console.log(Person.prototype.proto);
per实例对象的__proto__------->Person.prototype的__proto__---->Object.prototype的__proto__是null
console.log(per.__proto__==Person.prototype);
console.log(per.__proto__.__proto__==Person.prototype.__proto__);
console.log(Person.prototype.__proto__==Object.prototype);
console.log(Object.prototype.__proto__);
浏览器显示结果:
06 原型指向改变如何添加方法和访问
//人的构造函数
function Person(age) {
this.age=age;
}
//人的原型中添加方法
Person.prototype.eat=function () {
console.log("人正在吃东西");
};
//学生构造函数
function Student(sex) {
this.sex=sex;
}
//学生的原型中添加方法----先在原型中添加方法
Student.prototype.sayHi=function () {
console.log("您好哦");
};
//改变了原型对象的指向
Student.prototype=new Person(10);
var stu=new Student("男");
stu.eat();
stu.sayHi();
//人的构造函数
function Person(age) {
this.age=age;
}
//人的原型中添加方法
Person.prototype.eat=function () {
console.log("人正在吃东西");
};
//学生构造函数
function Student(sex) {
this.sex=sex;
}
//改变了原型对象的指向
Student.prototype=new Person(10);
//学生的原型中添加方法----先在原型中添加方法
Student.prototype.sayHi=function () {
console.log("您好哦");
};
var stu=new Student("男");
stu.eat();
stu.sayHi();
console.dir(stu);
如果原型指向改变了,那么就应该在原型改变指向之后添加原型方法
function Person(age) {
this.age = age;
}
//指向改变了
Person.prototype = {
eat: function () {
console.log("吃");
}
};
//先添加原型方法
Person.prototype.sayHi = function () {
console.log("您好");
};
var per = new Person(10);
per.sayHi();
07 实例对象的属性和原型对象中的属性重名问题
function Person(age,sex) {
this.age=age;
this.sex=sex;
}
Person.prototype.sex="女";
var per=new Person(10,"男");
console.log(per.sex);
因为JS是一门动态类型的语言,对象没有什么,只要点了,那么这个对象就有了这个东西,没有这个属性,只要对象.属性名字,对象就有这个属性了,但是,该属性没有赋值,所以,结果是:undefined
console.log(per.fdsfdsfsdfds)
console.log(fsdfdsfds);
实例对象访问这个属性,应该先从实例对象中找,找到了就直接用,找不到就去指向的原型对象中找,找到了就使用,找不到呢?=====
通过实例对象能否改变原型对象中的属性值?不能
就想改变原型对象中属性的值,怎么办?直接通过原型对象.属性=值;可以改变
Person.prototype.sex="哦唛嘎的";
per.sex="人";
console.log(per.sex);
console.dir(per);
08 一个很神奇的原型链
原型链:实例对象和原型对象之间的关系,通过__proto__来联系
var divObj=document.getElementById("dv");
console.dir(divObj);
divObj.proto
---->HTMLDivElement.prototype的__proto__
—>HTMLElement.prototype的__proto__-
—>Element.prototype的__proto__---->Node.prototype的__proto__---->EventTarget.prototype的__proto__---->Object.prototype没有__proto__,所以,Object.prototype中的__proto__是null
09 继承
面向对象编程思想
:根据需求,分析对象,找到对象有什么特征和行为,通过代码的方式来实现需求,要想实现这个需求,就要创建对象,要想创建对象,就应该显示有构造函数,然后通过构造函数来创建对象.,通过对象调用属性和方法来实现相应的功能及需求,即可
首先JS不是一门面向对象的语言,JS是一门基于对象的语言,那么为什么学习js还要学习面向对象,因为面向对象的思想适合于人的想法,编程起来会更加的方便,及后期的维护…
面向对象的编程语言中有类(class)的概念(也是一种特殊的数据类型),但是JS不是面向对象的语言,所以,JS中没有类(class),但是JS可以模拟面向对象的思想编程,JS中会通过构造函数来模拟类的概念(class)
小明,小红,小丽,小白,小花 都是人
共同的特征和行为
特征—>属性
行为—方法
面向对象的特性:封装,继承,多态
封装:就是包装
一个值存储在一个变量中–封装
一坨重复代码放在一个函数中–封装
一系列的属性放在一个对象中–封装
一些功能类似的函数(方法)放在一个对象中–封装
好多相类似的对象放在一个js文件中—封装
继承: 首先继承是一种关系,类(class)与类之间的关系,JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承
继承也是为了数据共享,js中的继承也是为了实现数据共享
原型作用之一:数据共享,节省内存空间
原型作用之二:为了实现继承
继承是一种关系:
父类级别与类级别的关系
例子:
小杨—>人, 姓名, 有钱, 帅, 有功夫–降龙十八掌
小杨子–>人,
继承:
姓氏----继承
外表----继承
财产----继承
功夫—继承
人: 姓名, 性别, 年龄 ,吃饭, 睡觉
学生类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 学习行为
老师类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资,教学行为
程序员: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资, 敲代码
司机类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资 开车
动物类别: 体重, 颜色, 吃
狗类别: 体重,颜色, 吃, 咬人
二哈类别: 体重,颜色, 吃, 咬人 逗主人开心,汪汪,你好帅
多态:一个对象有不同的行为,或者是同一个行为针对不同的对象,产生不同的结果,要想有多态,就要先有继承,js中可以模拟多态,但是不会去使用,也不会模拟,
例子:
人,都有姓名,性别,年龄, 吃饭, 睡觉, 玩
学生,都有姓名,性别,年龄, 成绩, 吃饭, 睡觉, 玩 ,学习的行为
js中通过原型来实现继承
function Person(name,age,sex) {
this.name=name;
this.sex=sex;
this.age=age;
}
Person.prototype.eat=function () {
console.log("人可以吃东西");
};
Person.prototype.sleep=function () {
console.log("人在睡觉");
};
Person.prototype.play=function () {
console.log("生活就是不一样的玩法而已");
};
function Student(score) {
this.score=score;
}
//改变学生的原型的指向即可==========>学生和人已经发生关系
Student.prototype=new Person("小明",10,"男");
Student.prototype.study=function () {
console.log("学习很累很累的哦.");
};
//相同的代码太多,造成了代码的冗余(重复的代码)
var stu=new Student(100);
console.log(stu.name);
console.log(stu.age);
console.log(stu.sex);
stu.eat();
stu.play();
stu.sleep();
console.log("下面的是学生对象中自己有的");
console.log(stu.score);
stu.study();
10 继承案例
动物有名字,有体重,有吃东西的行为
小狗有名字,有体重,有毛色, 有吃东西的行为,还有咬人的行为
哈士奇名字,有体重,有毛色,性别, 有吃东西的行为,还有咬人的行为,逗主人开心的行为
//动物的构造韩素
function Animal(name,weight) {
this.name=name;
this.weight=weight;
}
//动物的原型的方法
Animal.prototype.eat=function () {
console.log("天天吃东西,就是吃");
};
//狗的构造函数
function Dog(color) {
this.color=color;
}
Dog.prototype=new Animal("哮天犬","50kg");
Dog.prototype.bitePerson=function () {
console.log("哼~汪汪~咬死你");
};
//哈士奇
function ErHa(sex) {
this.sex=sex;
}
ErHa.prototype=new Dog("黑白色");
ErHa.prototype.playHost=function () {
console.log("哈哈~要坏衣服,要坏桌子,拆家..嘎嘎...好玩,开心不,惊喜不,意外不");
};
var erHa=new ErHa("雄性");
console.log(erHa.name,erHa.weight,erHa.color);
erHa.eat();
erHa.bitePerson();
erHa.playHost();
11 借用构造函数
function Person(name,age,sex,weight) {
this.name=name;
this.age=age;
this.sex=sex;
this.weight=weight;
}
Person.prototype.sayHi=function () {
console.log("您好");
};
function Student(score) {
this.score=score;
}
//希望人的类别中的数据可以共享给学生---继承
Student.prototype=new Person("小明",10,"男","50kg");
var stu1=new Student("100");
console.log(stu1.name,stu1.age,stu1.sex,stu1.weight,stu1.score);
stu1.sayHi();
var stu2=new Student("120");
stu2.name="张三";
stu2.age=20;
stu2.sex="女";
console.log(stu2.name,stu2.age,stu2.sex,stu2.weight,stu2.score);
stu2.sayHi();
var stu3=new Student("130");
console.log(stu3.name,stu3.age,stu3.sex,stu3.weight,stu3.score);
stu3.sayHi();
为了数据共享,改变原型指向,做到了继承—通过改变原型指向实现的继承
缺陷:因为改变原型指向的同时实现继承,直接初始化了属性,继承过来的属性的值都是一样的了,所以,这就是问题
只能重新调用对象的属性进行重新赋值,
解决方案:继承的时候,不用改变原型的指向,直接调用父级的构造函数的方式来为属性赋值就可以了------借用构造函数:把要继承的父级的构造函数拿过来,使用一下就可以了
借用构造函数:构造函数名字.call(当前对象,属性,属性,属性…);
解决了属性继承,并且值不重复的问题
缺陷:父级类别中的方法不能继承
function Person(name, age, sex, weight) {
this.name = name;
this.age = age;
this.sex = sex;
this.weight = weight;
}
Person.prototype.sayHi = function () {
console.log("您好");
};
function Student(name,age,sex,weight,score) {
//借用构造函数
Person.call(this,name,age,sex,weight);
this.score = score;
}
var stu1 = new Student("小明",10,"男","10kg","100");
console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score);
var stu2 = new Student("小红",20,"女","20kg","120");
console.log(stu2.name, stu2.age, stu2.sex, stu2.weight, stu2.score);
var stu3 = new Student("小丽",30,"妖","30kg","130");
console.log(stu3.name, stu3.age, stu3.sex, stu3.weight, stu3.score);
12 组合继承
原型实现继承
借用构造函数实现继承
组合继承:原型继承+借用构造函数继承
function Person(name,age,sex) {
this.name=name;
this.age=age;
this.sex=sex;
}
Person.prototype.sayHi=function () {
console.log("阿涅哈斯诶呦");
};
function Student(name,age,sex,score) {
//借用构造函数:属性值重复的问题
Person.call(this,name,age,sex);
this.score=score;
}
//改变原型指向----继承
Student.prototype=new Person();//不传值
Student.prototype.eat=function () {
console.log("吃东西");
};
var stu=new Student("小黑",20,"男","100分");
console.log(stu.name,stu.age,stu.sex,stu.score);
stu.sayHi();
stu.eat();
var stu2=new Student("小黑黑",200,"男人","1010分");
console.log(stu2.name,stu2.age,stu2.sex,stu2.score);
stu2.sayHi();
stu2.eat();
//属性和方法都被继承了
13 拷贝继承
拷贝继承;把一个对象中的属性或者方法直接复制到另一个对象中
var obj1={
name:"小糊涂",
age:20,
sleep:function () {
console.log("睡觉了");
}
};
//改变了地址的指向
var obj2=obj1;
console.log(obj2.name,obj2.age);
obj2.sleep();
var obj1={
name:"小糊涂",
age:20,
sleep:function () {
console.log("睡觉了");
}
};
var obj2={};
for(var key in obj1){
obj2[key]=obj1[key];
}
console.log(obj2.name);
function Person() {
}
Person.prototype.age=10;
Person.prototype.sex="男";
Person.prototype.height=100;
Person.prototype.play=function () {
console.log("玩的好开心");
};
var obj2={};
Person的构造中有原型prototype,prototype就是一个对象,那么里面,age,sex,height,play都是该对象中的属性或者方法
for(var key in Person.prototype){
obj2[key]=Person.prototype[key];
}
console.dir(obj2);
obj2.play();
14 总结继承
面向对象特性:封装,继承,多态
继承,类与类之间的关系,面向对象的语言的继承是为了多态服务的,
js不是面向对象的语言,但是可以模拟面向对象.模拟继承.为了节省内存空间
继承:
原型作用: 数据共享 ,目的是:为了节省内存空间,
原型作用: 继承 目的是:为了节省内存空间
原型继承:改变原型的指向
借用构造函数继承:主要解决属性的问题
组合继承:原型继承+借用构造函数继承
既能解决属性问题,又能解决方法问题
拷贝继承:就是把对象中需要共享的属性或者犯法,直接遍历的方式复制到另一个对象中
15 逆推继承看原型
function F1(age) {
this.age = age;
}
function F2(age) {
this.age = age;
}
F2.prototype = new F1(10);
function F3(age) {
this.age = age;
}
F3.prototype = new F2(20);
var f3 = new F3(30);
console.log(f3.age);//
16 函数的角色
//函数的角色:
//函数的声明
function f1() {
console.log("我是函数");
}
f1();
//函数表达式
var ff=function () {
console.log("我也是一个函数");
};
ff();
17 函数声明和函数表达式的区别
//函数声明
if(true){
function f1() {
console.log("哈哈,我又变帅了");
}
}else{
function f1() {
console.log("小苏好猥琐");
}
}
f1();
//函数表达式
var ff;
if(true){
ff=function () {
console.log("哈哈,我又变帅了");
};
}else{
ff=function () {
console.log("小苏好猥琐");
};
}
ff();
函数声明如果放在if-else的语句中,在IE8的浏览器中会出现问题
以后宁愿用函数表达式,都不用函数声明
18 函数中的this指向的问题
函数中的this的指向
普通函数中的this是谁?-----window
对象.方法中的this是谁?----当前的实例对象
定时器方法中的this是谁?----window
构造函数中的this是谁?-----实例对象
原型对象方法中的this是谁?—实例对象
严格模式:
"use strict";//严格模式
function f1() {
console.log(this);//window
}
f1();
普通函数
function f1() {
console.log(this);
}
f1();
定时器中的this
setInterval(function () {
console.log(this);
},1000);
构造函数
function Person() {
console.log(this);
对象的方法
this.sayHi=function () {
console.log(this);
};
}
原型中的方法
Person.prototype.eat=function () {
console.log(this);
};
var per=new Person();
console.log(per);
per.sayHi();
per.eat();
BOM:中顶级对象是window,浏览器中所有的东西都是window的
19 函数的不同的调用方式
普通函数
function f1() {
console.log("文能提笔控萝莉");
}
f1();
构造函数—通过new 来调用,创建对象
function F1() {
console.log("我是构造函数,我骄傲");
}
var f=new F1();
对象的方法
function Person() {
this.play=function () {
console.log("玩代码");
};
}
var per=new Person();
per.play();
20 函数也是对象
函数是对象,对象不一定是函数
对象中有__proto__原型,是对象
函数中有prototype原型,是对象
function F1() {
}
console.dir(F1);
console.dir(Math);//中有__proto__,但是没有prorotype
- 对象中有__proto__,函数中应该有prototype
- 如果一个东西里面有prototype,又有__proto__,说明是函数,也是对象
function F1(name) {
this.name=name;
}
console.dir(F1);
所有的函数实际上都是Function的构造函数创建出来的实例对象
var f1=new Function("num1","num2","return num1+num2");
console.log(f1(10,20));
console.log(f1.__proto__==Function.prototype);
所以,函数实际上也是对象
console.dir(f1);
console.dir(Function);
21 数组的函数调用
数组可以存储任何类型的数据
var arr=[
function () {
console.log("hls1183676168");
},
function () {
console.log("hls1183676168");
}
,
function () {
console.log("hls1183676168");
}
,
function () {
console.log("hls1183676168");
},
function () {
console.log("hls1183676168");
}
];
回调函数:函数作为参数使用
arr.forEach(function (ele) {
ele();
});
其他参考链接
【js高级 Day1】深入理解原型及作用,构造函数和实例对象和原型对象之间的关系
【js高级 Day2】深入理解原型添加方法,私有函数,面向对象的编程思想(案例小贪吃蛇)
【js高级 Day3】深入理解原型的方式继承,借用构造函数继承,组合继承,拷贝继承
【js高级 Day4】深入理解apply和call方法,作用域,闭包,递归
【js高级 Day5】深入理解浅拷贝,深拷贝,遍历DOM树,正则表达式
【如果你是新手】推荐链接
【 js基础 Day2】js的流程控制:分支语句,循环.顺序结构
【 js基础 Day3】关键字的使用,数组(重点)和函数(重点)
【 js基础 Day4】面向过程,面向对象,自定义对象,内置对象