概念 明确:面向对象几乎所有语言都有,它是一种编程思想
面向过程:一步步实现代码(就是咱们之前写的代码 面向对象:封装起来,然后创建对象调用(减少代码冗余,便于后期维护,减少内存占用等)举例说明:
造车
面向过程:想一个 造一下面向对象:封装起来(车模型—class),然后创建对象调用(造车—new object ) 好处(代码举例)贪食蛇游戏 由以上代码可看出,利用面向对象写的代码更方便后期维护
function 函数名() { // 留心1: 只要你写自定义构造函数了强烈推荐【大驼峰】 // 留心2:this创建的对象、也就是下面的obj1、obj2 // 留心3:切记切记切记别写返回值(返回基本类型等于白写、返回复杂类型自定义构造白写 } // 留心4:后期必须使用new关键词来创建对象 let obj1 = new 函数名() // 留心5:小括号有形参就写,没有写不写都行 let obj2 = new 函数名()
代码示例:
<script> // 需求:封装学生(姓名、年龄),然后创建对象使用 this就是创建的对象别忘了 // 代码 </script> <script> // 需求:封装学生(姓名、年龄),然后创建对象使用 this就是创建的对象别忘了 // 代码 // 普通对象 let obj1 = {uname:"张三", age:5} let obj2 = {uname:"李四", age:6} console.log(obj1, obj2) //------------------------------------------------------------------------------------------ // 面向对象:1 定义类 ES5叫构造函数 2 通过new创建对象 function Stu(name, age) { // 构造函数名推荐大驼峰 // this是创建的对象 就是obj3、obj4 this.uname = name this.age = age+2 // 不写返回值 字符串没意义白写return 复杂对象构造函数白写 // return 'sdfasdfasfd' // return {a:1} } let obj3 = new Stu('张三', 3) let obj4 = new Stu('李四', 4) console.log(obj3, obj4) </script>明确需求
发现:构造函数导致浪费存储空间
function Person (name, age) { this.name = name; this.age = age; this.say = function () { console.log('hello ' + this.name); } } var p1 = new Person('神龙教主', 18); //p1空间 {name: 教主, age:18, say:function(){}} var p2 = new Person('Jack', 16); //p2空间 {name:Jack, age:16,say:function(){} } 变量:内存 内存:栈(基本)、堆(复杂) 栈 堆 p1:地址1 地址1:{name:教主, age:18, say:function(){}} p2: 地址2 地址2:{name:Jack, age:16, say:function(){}} 发现:say冗余 前端:放到构造函数的公共空间解决:通过js给每个构造函数分配得公共空间 原型
概念
JS给每个函数函数分配了一个公共空间、这个公共空间就是原型
好处 节省内存
语法 注意:切记在构造函数外面用这个语法
增加一个值:构造函数.prototype.键 = 值 增加N个值: 方法1:构造函数.prototype.键 = 值 构造函数.prototype.键 = 值 方法2:构造函数.prototype = 对象 (留心:传地址 也就是将这个构造函数的公共空间转义 了。代码示例:
<script> function Person (name, age) { this.name = name; this.age = age; // this.say = function () { // console.log('hello ' + this.name); // } } Person.prototype.say = function () { console.log('hello ' + this.name); } var p1 = new Person('神龙教主', 18); //p1空间 {name:教主, age:18, say:function(){}} var p2 = new Person('Jack', 16); //p2空间 {name:Jack, age:16, say:function(){}} console.log(p1) console.log(p2) /* 栈 堆 p1:地址1 地址1:{name:教主, age:18, say:function(){}} 10G p2:地址2 地址2:{name:Jack, age:16, say:function(){}} 10G 发现:say方法冗余 占用内存 因为代码一样 解决:原型 公共空间 */ </script>画图分析:
是什么:JS给每个函数分配的公共空间
好处:减少内存占用
存语法:
构造函数名.prototype.键 = 值 构造函数名.prototype.键 = 值 构造函数名.prototype = 对象(注:传地址 就是改变函数的公共空间)取:new创建的对象._proto_.键 (不推荐!!!
概念:
多个原型的集合
明确需求 发现:下述语法不报错
<script> function Stu(){} var stuObj = new Stu; stuObj.toString() // 思考:为什么不报错? console.log(stuObj) </script>原因:因为原型链
原型链特性:
当你调用对象的属性或方法时 先 自身找 找到了-直接用,找不到-去上一个公共空间找/也就是原型链上找 … 找到了-直接用,找不到-去原型链上找 … 一直知道顶级公共空间 Object对象的公共空间 找到了 - 直接 找不到 - 属性 undefined 方法 报错 留心:基于类创建对象 除了当前构造函数公共空间、还有底层默认的公共空间
比如数组就有数组的公共空间 源于Array构造函数 最底层Object公共空间
练习:
a、打印上述代码验证原型链的存在b、创建数组、查看其原型链c、定义构造函数Stu、对象默认msg属性、并且原型上增加msg属性验证优先级d、搞事情给数组绑定sumFn方法、自动实现实参求和 <script> // - a、打印上述代码验证原型链的存在 // - b、创建数组、查看其原型链 数组 Array公共空间 Object公共空间 没有 // - c、定义构造函数Stu、对象默认msg属性、并且原型上增加msg属性验证优先级 function Stu(){ this.msg = '构造函数的msg' } Stu.prototype.msg = 'Stu原型上的msg' let obj1 = new Stu console.log(obj1) //打印出Stu这个对象 console.log(obj1.msg) // 构造函数的msg 如果把构造函数中的msg注释了就是undefined console.log(obj1.msg123123) // undefined obj1.msg123123() // 报错 //-------------------------------------------------------------------------------------------- // - d、搞事情给数组绑定sumFn方法、自动实现实参求和 let arr = [11,22] // let arr = new Array([11,22]) Array.prototype.sumFn = function() { // this === 构造函数中的this == new出来的对象 // arr console.log(this) // [11,22] let sum = 0 for (let i = 0; i<this.length; i++) { sum+=this[i] } console.log(sum) } console.log(arr) arr.sumFn() // 先自身 然后Array构造函数 公共空间 有了就停止 如果没有就Object构造函数公共空间 </script>原型链是什么:多个原型的集合 原型链特性:
先找自身 接着原型链上找 一直找到顶级Object构造函数的公共空间 有-用 没有-属性undefined 方法报错
明确细节
obj.键 解释(推荐):调用对象属性,先自身没有就去原型链 obj.\__proto__.键 = 666 解释(不可能用):直接修改公共空间里面的数据 obj.键 = 666 解释:仅仅操作自己(有就修改、没有就新增结论:
留心1:对象获取数据不写__proto__、因为原型链特性 留心2:obj.键 只能用 不能改. 但是你就想修改原型链上的数据 通过 函数名.prototype.键 = 值严谨图 找公共空间:
显示dog对象 -> .__proto__ -> dog对象构造函数公共空间 -> -> .__proto__ -> Object构造函数的公共空间 -> -> .__proto -> null找属性和方法的情况
obj.属性 -> 自身找 -> 自身公共空间找 -> 顶级公共空间 找到了就用 找不到 属性就是undefinedhttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function
每个JavaScript函数实际上都是一个function对象。运行(function(){}).constructor===function便可得出这个结论。
题目1
// 自定义Fn1构造函数 function Fn1() { } Fn1.prototype.msg = 'hello' // 在Fn1原型对象/公共空间上 增加一个msg属性 叫hello let obj1 = new Fn1 let obj2 = new Fn1 console.log(obj1.uname) // 先自身 没有 再原型链 没有 输出 undefined console.log(obj1.msg) // 先自身 没有 再原型链 有 输出 hello console.log(obj2.msg) // 先自身 没有 在原型链 有 输出 hello obj1.msg = '神龙迷雾' // 修改自身的msg值 思考:原型链不会变 console.log(obj1.msg) // 神龙迷雾 console.log(obj2.msg) // hello题目2
function Fn2() { // 定义Fn2构造函数 } Fn2.prototype.msg = 'Kitty' // 在 Fn2 原型上/公共空间上 增加一个msg属性 let obj1 = new Fn2 let obj2 = new Fn2 // console.log(obj1.hello()) // 先自身 没有 再原型链 没有 报错 console.log(obj1.msg) // 先自身 没有 再原型链 有 Kitty console.log(obj2.msg) // 先自身 没有 再原型链 有 Kitty obj1.__proto__.msg = '神龙迷雾' // 仅仅练习 实战不用 修改了Fn2的公共空间 神龙迷雾 console.log(obj1.msg) // 先自身 没有 再原型链 有 神龙迷雾 console.log(obj2.msg) // 先自身 没有 再原型链 有 神龙迷雾题目3
<script> function Fn3() { } Fn3.prototype.msg = '神龙教主' let obj1 = new Fn3 let obj2 = new Fn3 console.log(obj1.msg) // 先自身 没有 原型链 有 输出神龙教主 console.log(obj2.msg) // 先自身 没有 原型链 有 输出神龙教主 Fn3.prototype.msg = '洪福齐天' // 构造函数改 肯定实在原型链 // 对象取上原型链 对象改 就没有原型链这一说 console.log(obj1.msg) // 先自身 没有 原型链 有 洪福齐天 console.log(obj2.msg) // 先自身 没有 原型链 有 洪福齐天 </script>题目4
<script> function Fn4() { } Fn4.prototype.msg = '神龙教主' let obj1 = new Fn4 let obj2 = new Fn4 // console.log(obj1.msg) // 神龙教主 console.log(obj2.msg) // 神龙教主 let tempObj = {uname:'aaa', age: 19, msg: '你好'} // Fn4.prototype = tempObj // console.log(obj1.msg) // 神龙教主 console.log(obj2.msg) // 神龙教主 console.log(Fn4.prototype) // {uname:'aaa', age: 19, msg: '你好'} </script>分析: 题目5(最难):
<script> var father1 = { name:"Rs" } var father2 = { name:"Tom" } var Son = function (){} // Son.prototype = father1; // var son1 = new Son() console.log('son1.name:', son1.name) // Rs Son.prototype = father2 // console.log('son1.name:', son1.name) // Rs var son2 = new Son() // console.log('son2.name:', son2.name) // Tom son2.name = "Jack" // console.log('son2.name:', son2.name) // Jack console.log('son2.__proto__.name:', son2.__proto__.name) // Tom console.log('son2.name:', son2.name) // Jack console.log('father2.name:', father2.name) // Tom </script>概念 constructor 打印对象所属构造函数
instanceof 检测构造函数 在对象的原型链上
语法 对象.constructor 简写 原型链会向上找属性
对象 instanceof 构造函数
练习
<script> let arr = [11, 22] console.log(arr) console.log(arr.constructor) // 先自身没有再原型上 // 作用:打印原型所属构造函数 // 应用:判断是否是数组 // 语法:对象 instanceof 构造函数 // 作用:检测 构造函数 是否在对象的原型链上 console.log(arr instanceof Array) // true console.log(arr instanceof String) // false console.log(arr instanceof Object) // true </script>小总结 对象.constructor 获取对象的所属构造函数 (注:用的少 ,但是这个单词很重要
对象 instanceof 构造函数 检测否则函数是否在对象的原型链上 (应用判断是否是数组