web前端开发笔记整理(五)

JS继承模式

ECMAScript不支持接口继承,只支持实现继承
继承就是获取存在对象已有属性和方法的一种方式.简单来说,A对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。

为什么要继承?

1.传统模式(原型链)

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针,让这个原型对象(子的原型)等于要继承的引用类型(父)的实例,由于引用类型(父)的实例包含一个指向(父)原型对象的内部指针,以此类推,层层递进,便构成实例与原型的链条,即原型链。

优点:
将父类的实例作为子类的原型,可以方便的基础父类型的原型中的方法;

缺点:
过多的继承了没有用的属性
只执行一次,无法给属性传值

//父类型
function Person(name,age,sex){
	this.name=name;
	this.age=age;
	this.sex=sex;
	
}
//子类型
Person.prototype.sayHi=function(){
	console.log('大家好,我是'+this.name);
}
function Student(){
	this.score=100;
	
}
Student.prototype= new Person();
Student.prototype.constructor=Student;

var s1=new Student();
say.sayHi()

在这里插入图片描述
在这里插入图片描述

属性的继承没有意义
注意点:
(1)给原型添加方法一定要放在替换原型的语句之后
(2)在通过原型链实现继承时,不能使用对象字面量创建原型方法

原型链存在的问题:
(1)子类型的所有实例都可以共享父类型的属性
(2)子类型的实例无法在不影响所有对象的情况下,给父类型的构造函数传递参数

2.构造继承(伪造对象或经典继承)

在子类型构造函数的内部调用超类型构造函数(通过使用apply()和call()方法也可以在(将来)新创建的对象上执行构造函数);

优点

  • 创建子类实例时,可以向父类传递参数
  • 可以实现多继承
  • 可以方便的继承父类型的属性,但是无法继承原型中的方法

缺点:

  • 实例并不是父类的实例,只是子类的实例

  • 只能借用方法,不能借用原型

  • 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

Person.prototype.lastname = 'deng';
Person.prototype.hobbit = 'running';
function Person(name,age,sex){
    	this.name = name;
    	this.age = age;
    	this.sex = sex;
    }
    function Student(name,age,sex,tel,grade){
    	// var this = {name:"",age :"", sex:""}
    	Person.call(this,name,age,sex);
    	this.tel = tel;
    	this.grade = grade;
    }
    var student = new Student('sunny',123,'male',139,2017);
    console.log(Student.hobbit)//undefiend
    console.log(Student.lastname)//undefiend

方法都在构造函数中定义,函数复用变得没有意义

1.就是虽然Student()利用了call实现了对Person()功能的继承,可是它不能借用构造函数的原型

2.每当需要用call来实现功能的借用的时候,都要去执行函数一次。

3.共享原型(标准)

函数.prototype = 函数.prototype
不能随便改动自己的原型

Father.prototype.lastName = "Deng";
function Father() {
}
function Son() {
}
Son.prototype = Father.prototype;
var father = new Father();
var son = new Son();
console.log(son.lastName );//Deng

在这里插入图片描述

这种共享原型的方式,Father和Son同时指向同一个原型,
·它们共用的一个原型,一个更改了原型上的属性,其他的都会更改。

现在我们用一个函数来对上面的例子进行封装

Father.prototype.lastName = "Deng";
function Father() {
}
function Son() {
}
function inherit(Targrt, Origin){
    Targrt.prototype = Origin.prototype;
}
inherit(Son,Father);
Son.prototype.sex = 'male'
var son = new Son()
var father = new Father();

在这里插入图片描述

当我们增加Son的原型的时候,Father的原型也就跟着增加了,可是我们并不想增加一个函数的原型的时候,另一个函数的原型也跟着增加,

所以就引出了我们的圣杯模式

圣杯模式

在这里插入图片描述
就是构建一个构造函数F,

F.prototype =Father.prototype
Son.prototype = new F();
Father.prototype.lastName = "Deng";
function Father() {}
function Son() {}
function inherit(Target, Origin){
    function F() {} //新建一个空的构造函数
    F.prototype = Origin.prototype;//让这个空的构造函数(F)的原型指向原始函数(Father)的原型
    Target.prototype = new F();//让要继承函数(Son)的原型指向这个空函数的原型,这样这个要继承别人的函数添加自己原型属性,也不会污染那个原始函数。
}
inherit(Son,Father);
var son = new Son()
var father = new Father();
//这样就形成了Son继承子F,F继承自Father。

在这里插入图片描述

查看son的构造函数是Father(理应应该是Son),这里是有一个过程的:
son.proto—>new F(),对象上没有constructor ,
再往上找 new F().proto---->Father.prototype,
Father.prototype上有constructor,它指向Father,
这样就指向紊乱了。需要给它归位。如下:


// JavaScript Document
function Father() {}
function Son() {}
function inherit(Target, Origin){
    function F() {} 
    F.prototype = Origin.prototype;
    Target.prototype = new F();
    
    Target.prototype.constructor = Target //让对象的constructor 属性指向自己
}
inherit(Son,Father);
var son = new Son()
var father = new Father();

在这里插入图片描述

问题:如果把F.prototype = Origin.prototype;和 Target.prototype = new F();换个位置还能实现这种继承模式吗?

function inherit(Target, Origin){
    function F() {} 
    Target.prototype = new F();
    F.prototype = Origin.prototype;
}

这样就不行了,因为,new F()的是原来的那个原型,new完了再改让它指向Origin.prototype已经晚了。

在看一个应用的变量的的小栗子:

function inherit(Target, Origin){
    function F() {} 
    F.prototype = Origin.prototype;
    Target.prototype = new F();    
    Target.prototype.constructor = Target
    Target.prototype.uber = Origin.prototype; 
}
var inherit = (function () {

    var F = function () {};
    return function (Target, Origin){
        F.prototype = Origin.prototype;
        Target.prototype = new F();
        Target.prototype.constructor = Target   
        Target.prototype.uber = Origin.prototype;
    }
}());

这两种方式下面的一种是闭包的封装,实现F函数的私有化
var F = function () {};这里的F函数执行完之后就会销毁,但是他传递原型断开映射的功能以及实现,他就成为了私有变量或私有函数
F函数本来就是用来过渡的
(建议以后的圣杯模式就写成下面这种方式。)

对象枚举

仿jquery封装`连续调用`

var jq = {
    a : function () {
        console.log("aaa");
        return this;//在每个函数的末尾返回this对象,可以实现对象连续调用多个方法
        //因为this就是谁调用这个函数,函数里面的this就指向谁。
    },
    b : function () {
        console.log("bbb");
        return this;
    },
    c : function () {
        console.log("ccc");
        return this;
    }
}
jq.a().b().c();//aaa bbb ccc

对象访问属性

  • 对象.属性–>obj.name
  • 对象[字符串形式的属性名]---->obj[‘name’]
stu = {name:xm, age:18};
var age1 = stu.age;
var age2 = stu["age"];
alert(age1 == age2);//true

内部原理:
每当你访问 obj.name 的时候,系统隐式的访问的是 obj[‘name’];
直接使用这样的方式obj[‘name’]更方便,因为内部就是这样执行的,访问速度更快。

示例:实现输入索引就输出对应索引属性名属性的值(就是实现属性名的拼接)

var q = {
    a1 :{name : "dong"},
    a2 :{name : "nan"},
    a3 :{name : "xi"},
    a4 :{name : "bei"},
    sayA :function (num){
        return this['a' + num];
        //实现属性名的拼接,只能用对象['属性名' + ...]的方式
    } 
}
console.log(q.sayA(1));//Object { name: "dong" }
console.log(q.sayA(2));//Object { name: "nan" }
console.log(q.sayA(3));//Object { name: "xi" }
console.log(q.sayA(4));//Object { name: "bei" }

对象的枚举

例如给你一组数据,找出每一个数据就是遍历枚举

var obj = {
    name : '13',
    age : 89,
    sex : "male",
    height : 180,
    weight : 75
}
for(var prop in obj) {//通过对象的个数控制循环圈数
   console.log(prop + " " + typeof(prop));
   //name string
   //age string
   //sex string
   //height string
   //weight string
      
   console.log(obj.prop); //undefined undefined undefined undefined undefined
 //这个时候系统是把prop当做obj的一个属性,因为它没有这个属性所以会输出undefined
   console.log(obj[prop]);
  
}

用obj[prop]就是正确的,这样就会把它当做一个变量来看
不能加上obj[‘prop’],prop现在本来就是一个字符串

hasOwnProperty 方法

  • hasOwnProperty(),括号里面传进去的是要判断属性名的字符串形式,返回布尔值
  • hasOwnProperty就是过滤性掉原型上的属性,留下只属于自己的属性
var obj = {
    name : '13',
    age : 89,
    sex : "male",
    height : 180,
    weight : 75,
    __proto__ : {//手动给原型上添加一个属性
        lastName  : "deng"
  }
}

Object.prototype.abc = "123";
for (var prop in obj){
   for (var prop in obj){
   if(obj.hasOwnProperty(prop)){//过滤性掉原型上的属性
    console.log(obj[prop]);//13 89 male 180 75
   }   
}
for (var prop in obj){
    //!hasOwnProperty
   if(!obj.hasOwnProperty(prop)){
    console.log(obj[prop]);//deng 123
   }   
}

in属性(很少用)

  • in 只能判断这个对象能不能访问到这个属性 ,包括原型上的。(父类的也就返回)
  • in 的语法格式就是前面就是这个属性的字符串形式,不能直接用属性名
console.log('height' in obj);//true
console.log('lastName' in obj);//true 
console.log(height in obj);//ReferenceError: height is not defined

注意最后一种情况,属性一定要加双引号

instanceof 属性

function Person() {
}
var person = new Person();
// A对象是不是B构造函数构造出来的?
//看A对象的原型链上有没有B的原型
// A instanceof B
console.log(person instanceof Person);//true
console.log(person instanceof Object);//true
console.log({} instanceof Object);//true
console.log(person instanceof Array);//false

区分数组还是对象,var arr = [] || {}?

方法一:判断constructor方法是否一致
方法二:既然数组是一种对象,那么数组的原型链上一定有对象的原型,而对象的原型链上没有数组的原型,instanceof 方法
方法三:使用Object.prototype.toString.call([]),将[]数组按照Object的原型的toString的方法打印

var arr = [] || {};//区分是数组还是对象
//1,用constructor方法:[].constructor是function Array(), obj.constructor是 function Object()
//2,用instanceof方法:[] instanceof Array是true, obj instanceof Array是 false
//3,用toString,各个构造函数重写了toString方法

Object.prototype.toString.call([]);
//谁调用this,this就是谁
Object.prototype.toString = function () {
    // 识别this
    // 返回相应的结果
}
// obj.toString();

命名空间

var name="bcd";
var init=(function(){
	var name="abc";
	function callName(){
		console.log(name);
		
	}
	return function(){
		callName();
	}
}())
init();

输出结果是abc

在外面定义的name与内部name不互相影响,函数在外部调用内部函数函数形成了闭包,init()代表callname()功能,变量私有化不会污染全局变量。

习题穿插

第一题
Question: javaScript中定义函数的方式有_______, ______, _______.
1,函数声明 2,函数的表达式 3,Function构造器

 var fn = new Function('var a = 10;console.log(a)');
fn();
 var fn1 = function(){
 var a  = 10 ;
 console.log(a);
      }
   funciton Function(){}  产生匿名的函数
   // var obj = new Object()

第二题

   var value = "web"
      function bar() {
        var value = "duyi";

        function foo() {
            console.log(value);
        }
        return foo;
    }
    bar()()//变成了 (function foo(){ console.log(value)})()
    

问输出什么
duyi

第三题
Question: 实现一个函数addCounter,用于返回累加n操作的函数

function add(a) {
    function sum(b) { // 使用闭包
        a = a + b; // 累加
        return sum;
    }
    sum.toString = function() { // 重写toString()方法,返回sum值
        return a;
    }
    return sum; // 返回一个函数
}

在这里插入图片描述

习题:

在这里插入图片描述
输出什么?
function里面变成表达式会立刻执行, 整个函数转换成true,f已经是undefined,在执行x+=typeof f;这时候f已经没得了,但惟独放在typeof里面不报错,1+undefined就是undefined。
输出undefined

this

this指向的几种情况:

函数预编译过程 this 指向 window

function test(c){
    // var this = Object.create(test.prototype);第一种最标准的隐式this的写法
    //var this = {                              第二种方法
    //     __proto__ : test.prototype
    // }
    var a = 123;
    function b() {}
}
//预编译阶段生成AO对象,
//AO对象不仅有变量的声明提升和函数声明提升,还有arguments实参列表和this指向window
// AO {
//     arguments : [1],
//     this : window,
//     c : 1,
//     a : undefined,
//     b : function () {}
// }
 test(1);
 
 new test();

当new一个对象的时候,调用函数的时候,
在函数内部生成一个隐式的this对象(var this = Object.create(test.prototype);)去覆盖AO里面的this

function test() {
    console.log(this);
}
test();

打印this其实就是window,打印window和它是一样的。
全局作用域里 this 指向 window

在全局作用域也就是GO对象里面也有一个this,它自然是指向window的。
在这里插入图片描述call/appy可以改变函数运行时的this指向
obj.function(), function里面的this指向obj

var obj = {
    a : function () {
        console.log(this.name);
    },
    name : 'abc'
}
obj.a();//abc 

this笔试题

var name = "222";
var a = {
    name : "111",
    say  :function () {
        console.log(this.name);
    }
}
var fun = a.say;
fun()
a.say()
var b = {
    name : "333",
    say : function (fun) {
        fun();
    }
}
b.say(a.say);
b.say = a.say;
b.say();

分析:

var name = "222";
var a = {
    name : "111",
    say  :function () {
        console.log(this.name);
    }
}
var fun = a.say;//a.say代表函数的引用,代表函数体
fun();//就是把
// function () {
//         console.log(this.name);
//     }放到全局范围内执行,也没人调用它,所以this指向window,所以打印222

a.say()// 这个就是对象调用这个方法,this指向对象a,打印的就会是111
var b = {
    name : "333",
    say : function (fun) {
 
        //this---->b,333
        fun();
    }
}
b.say(a.say);//b.say,对象b调用它的say方法,现在b的say方法里面的this指向b
//然后里面放进去一个a.say,也就是把function () {
//         console.log(this.name);
//     }放到b的say函数里面执行,这个函数执行只不过是在另外一个函数里面,也没人调用它,这个时候走的就是预编译的环节,this它就指向window,打印出来的就是222

b.say = a.say;//把a.say放到b.say里面,那就是var b = {
                                        //     name : "333",
                                        //     say : function () {
                                        //          console.log(this.name);
                                        //     }
                                        // }
b.say();//再调用b.say方法当然是this指向自己,打印333

arguments

  • arguments 是一个类似数组的对象, 对应于传递给函数的参数。
    找到最大的一个参数的值:
x = findMax(1, 123, 500, 115, 44, 88);
 
function findMax() {
    var i, max = arguments[0];
    
    if(arguments.length < 2) return max;
 
    for (i = 0; i < arguments.length; i++) {
        if (arguments[i] > max) {
            max = arguments[i];
        }
    }
    return max;
}

创建一个函数用来统计所有数值的和:

x = sumAll(1, 123, 500, 115, 44, 88);
 
function sumAll() {
    var i, sum = 0;
    for (i = 0; i < arguments.length; i++) {
        sum += arguments[i];
    }
    return sum;
}

arguments.callee

function test{
console.log(arguments.callee==test)
}
test();

打印结果是true
callee是arguments对象的属性。在函数体内,它指向当前正在执行的函数。

实现10的阶乘

var num=(function (n){
	if (n==1){
		return 1;
	}
	return n * arguments.callee(n-1);
}(10))
function test() {
    conso.log(arguments.callee);//打印test函数
    function demo () {
        conso.log(arguments.callee);//打印demo函数
    }
    demo();
}
test();

就是说在哪个函数里面的arguments.callee就指向那个函数的引用。

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章