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

对象

JavaScript不区分类和实例的概念
创建一个name对象

var name=“小明”;

创建一个函数的时候,函数也是对象

function foo() {
    return 0;
}

创建对象的两种方式

  • var obj = new object();
  • var obj = {};

写个小demo感受一下:

var test={
	name:"xiaohua",
	age:"18",
	sex:"male",
	health:100,
	smoke:function(){
		console.log("i am smoking")
		this.health--;
		console.log(this.health)
	},
	drink: function(){
		console.log("i am drining")
		this.health++;
	
	}
}

这里创建了一个叫test的对象,里面装了叫小花的人名,性别,性别。
定义了两个函数体,分别用来控制小花的健康,抽烟呢健康就在100的基础上-1,喝酒呢就加一。

我们在控制台查看
在这里插入图片描述

对象的增删查改

在上面的代码上进行

”增加“
在这里插入图片描述
"删除"
一定要用delete

在这里插入图片描述

"查找"
在这里插入图片描述
”改”
也就是在原有基础上给变量赋值;
在这里插入图片描述

构造函数

构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象。 即为对象变量赋初始值。每个构造函数的实例都将共享构造函数的初始值。 构造函数的出现是为了解决使用Object构造函数和字面量表示法不方便创建大量重复对象的问题。

构造函数的内部原理

  • 在函数体前面隐式的加上this = {};

  • 执行this.xxx = xxx ;

  • 隐式返回this

构造函数的定义的格式:

 修饰符  函数名(形式参数){
        函数体...
    }
  • 构造函数 是没有返回值类型的。
  • 构造函数的函数名必须要与类名一致
  • 构造函数需要以一个大写字母开头,而非构造函数应该以一个小写字母开头,这个主要是为了区别构造函数和其它函数
  • 构造函数其实本身也是函数,只是用来创建对象
  • 调用构造函数千万不要忘记写new
  • 如果一个类没有显式的写上一个构造方法时,那么java编译器会为该类添加一个无参的构造函数的。

任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数 ;

function Student(name) {
    this.name = name; //只在被实例化后的实例中可调用

    this.hello = function () {
        alert('Hello, ' + this.name + '!');
    }
}
var xiaoming = new Student('小明');//this指向小明
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!

在这里插入图片描述

普通函数与构造函数的区别“

1. 返回值类型的区别:

    1. 构造函数是没有返回值类型 的,
    2. 普通函数是有返回值类型的,即使函数没有返回值,返回值类型也要写上void。

2. 函数名的区别:

 1. 构造函数的函数名必须要与类名一致,
  2. 普通函数的函数名只要符合标识符的命名规则即可。

3. 调用方式的区别:

 1. 构造函数是 在创建对象的时候由jvm调用的。
 2. 普通函数是由我们使用对象调用的,一个对象可以对象多次普通 的函数,

4. 作用上的区别:

  1. 构造函数 的作用用于初始化一个对象。
  2. 普通函数是用于描述一类事物的公共行为的。

包装类

原始值没有属性和方法,但是如果强行添加属性或者访问属性的话,系统就会新建一个包装类,然后在包装类上进行操作,操作完成后进行销毁

JS的数据类型:

**值类型(基本类型)**字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

引用数据类型对象(Object)、数组(Array)、函数(Function)。

JS中只有对象才有属性和方法,原始值没有属性和方法
给基本类型添加属性和方法:

//给字符串添加方法  要写到对应的包装对象的原型下才行
  var str= "hello world";
  //若我们想在原型上设置一个属性long 保存字符串的长度
  //var str = new String("hello world");// 1.找到基本包装对象,创建一个和字符串值相同的对象,
  //String.prototype.long=str.length; // 2.通过这个对象找到了包装对象下的方法并调用 
  //str=null; //  3.这个刚创建的对象被销毁
  String.prototype.long=str.length;//  执行到这一句,同样因为没有length属性 后台会偷偷的执行上述三步操作
  console.log(str.long); //结果为:11
var str= "hello world";
var str2="我的长度也为11吗?";
  String.prototype.long=str.length;
  console.log(str2.long);   //结果为:11

这样因为你是给基本类型string的原型上添加的属性,所以任意一个字符串都可以访问到这个属性及此值。(所以不提倡这种做法)

原始值没自定义有属性和方法 不能给原始值赋值(赋值也不会报错)
js中提供了三种特殊的引用类型(String Number Boolean)每当我们给原始值赋属性值时 后台自动调用包装类转换
String 默认有length属性而且不可赋值

var str = 'abc';
str+=1; //str = 'abc1';
var test = typeof str;//test = 'string';
if(test.length == 6){
// var obj = new String(;'abcd1');
// obj.sing = true;
// delete obj;
test.sign= true;
}
// var obj = new String(;'abcd1');
// console.log(obj.sing);
// delete obj;
console.log(test.sign);// undefined

在这里插入图片描述

test是字符串string test.length == 6 就是对的, test.sign会把test调用,然后test.sign输出true???
但是原始值是不能操作属性的,原始值赋属性值要调用包装类

test是原始值,不会有length属性
代码执行在test.sign处会new string(test).sign=true
进行包装类之后就会删除销毁,所以到最后一句系统看test原始值没有sign属性,又会包装一遍new string(test).sign。

但这次操作跟上次操作不一样,系统执行语句进行包装类,执行完包装类之后就会销毁,在执行语句又会在创建包装类,那么你的这个新的包装类中并没有sign的属性。

因为进行了包装类已经将test变成一个构造对象了,打印对象中的属性,即使我并没有这个属性,系统一样不会报错,只会提示undefined。
所以输出undefied

var x =1,y=z=0;
function add(n){
return n =n+1;
}
y=add(x);
function add(n){
return n = n+3;
}
z=add(x);

xyz分别输出
输出1,4,4,
这里存在一个函数提升,第二个add(n)覆盖第一个add(n)

原型

  • 原型是function对象的一个属性,它定义了构造函数制造出的对象的公共先祖。
  • 通过该构造函数产生的对象,可以继承该原型的方法。
  • 原型也是对象

原型的应用

提取共有属性,避免代码冗余
例如

function Car(color,owner){
  this.color = color;
  this.owner = owner;
  this.carName = "bmw";
  this.height = 1400;
  this.lang = 4900;
  
}
var car = new Car("red","bmw",1400,4900,"prof.ji");
var car1 = new Car("blue","bmw",1400,4900,"long");

在这段代码中car和car1和carName属性值都是相同的,我们可以把这些属性赋给函数原型

Person.prototype = {
  height:1200;
  lang:4900;
  carName : "bmw";
}
function Car(color,owner){
  this.owner = owner;
  this.color = color;
}
var car = new Car("red","prof.ji");
var car1 = new Car("blue","long");

对象修改原型的属性时只能通过调用原型的属性进行改变

Person.prototype.lastname = "deng";
function Person(name){
  this.name = name;
}
var person = new Person("sumong");
Person.prototype.lastname = "liu";  //改变原型的属性

原型的几个属性:

prototype属性:我们只要创建了一个函数,就会根据一组特定的规则为这个函数创建一个prototype属性。这个属性指向函数的原型对象。

constructor属性:创建了一个自定义构造函数后,其原型对象默认只会取得constructor属性。指向了构造函数。

_proto_属性:(这是一种隐式命名规则,是系统命名的): 这个属性指向了构造方法的原型对象。

1.prototype

Person.prototype.name="xiaohua";
function Person(){
}
var person =new Person();
var person1=new Person();

在这里原型是Person.prototype
祖先是Person.prototype={ }
在这里插入图片描述

  • new Person()创建多个对象person、person1,则多个对象都会同时指向Person构造函数的原型对象。
  • prototype在构造函数出生的时候就已经被定义好了
  • 如果我们访问person中的一个属性name,如果在person对象中找到,则直接返回。如果person对象中没有找到,则直接去person对象的__proto__属性指向的原型对象中查找
  • 原型相当于函数产生对象的隐形父级,是可以访问的

2.constructor查看对象的构造函数

任何一个prototype对象都有一个constructor属性,指向它的构造函数。每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。

function Person(){
}

Car.prototype.say = "def";
function Car(){

}
//Car.prototype = {
//    constructor : Person
//}
var car = new Car();
console.log(car.constructor);//function Car(){}

//console.log(car.constructor);//function Person(){}

//注释掉的部分:可以更改对象car的构造函数,
//            它本来是function Car(){}实例化的一个对象,
//            现在Car.prototype = {constructor : Person}让它指向构造函数function Person(){}

3._proto_,查看原型

new一个构造函数,相当于实例化一个对象,这期间其实进行了这三个步骤:

  • 在构造函数的逻辑最顶端隐式的新建一个this对象,this其实不是不是一个 空对象,
    var this = { proto : Person.prototype}//__proto__属性 指向的是对象原型。 (每个对象都有__proto__属性,该属性指向一个对象,就是构造函数Person的原型对象(Person.prototype))

  • 去调用构造函数Person,从而设置对象的属性和方法并初始化。并返回this对象。(把this返回,这样每一个实例化的对象就有__proto__属性了。)

  • 上面步骤完成后,这个对象就与构造函数Person再无联系,这个时候即使构造函数Person再加任何成员,都不再影响已经实例化了的对象了。(此时该对象有
    了自己的属性之后,同时具有了构造函数Person的原型对象的所有成员)

a.用法:

Person.prototype.name = 'abc';
function Person(){
    //var this = {
    //  __proto__ : Person.prototype//指向的是对象原型
   // }
}
var obj ={
    name : "sunny"
}   
var person = new Person();
person.__proto__ = obj;
console.log(person.name);

在这里插入图片描述

当new一个对象的时候,开始查找属性name,
先看自己的构造函数里面有没有name属性,如果有就直接用,
如果没有就沿着this里面__proto__ 属性去对象的原型里面查找,这个时候我改变了person.proto = obj;让它指向对象obj,值就是sunny

b.’.'的写法改变原型对象属性的值,那么结果也会跟着改

Person.prototype.name = 'sunny';

function Person(){
//var this = {__proto__ : Person.prototype}
}

// Person.prototype.name = 'cherry';

var person = new Person();

Person.prototype.name = 'cherry';

console.log(person.name)

无论Person.prototype.name = 'cherry’放在var person = new Person();上面还是下面,输出都是cherry,因为它改变的是__proto__ 指向的Person.prototype上的值。值都变了,person.name必然会变化。

c.改变原型让它指向另外的一个空间

var obj = {name : "a"};
var obj1 = obj;//obj和obj1先指向同一个房间,
obj = {name : "b"};//obj它又指向另外一个房间
console.log(obj1.name);//a
console.log(obj.name);//b

Person.prototype.name = 'sunny';
function Person(){
//var this = {__proto__ : Person.prototype}
}
var person = new Person();
Person.prototype = {          //这是把原型给改了,换了一个新对象
    name : 'cherry'
}
console.log(person.name)//sunny

首先new一个对象person,调用构造函数,里面隐式的var this = {proto : Person.prototype}
让__proto__ 和 Person.prototype指向同一个空间,然后返回this,这个对象就构建完了。
然后Person.prototype = { name : ‘cherry’ }把自己的空间换了,但是 proto 没有换,__proto__它还是指向原来的Person.prototype的空间值就是sunny。

那么查找name属性的时候,去__proto__里面找到Person.prototype.name ,结果就是sunny.。
就像下面的过程:

 Person.prototype = {name : "sunny"};
 _proto_ = Person.prototype;
 Person.prototype = {name : "cherry"};
 console.log(_proto_.name);//sunny

Person.prototype.name = 'sunny';
function Person(){
//var this = {__proto__ : Person.prototype}
}
Person.prototype = {         
    name : 'cherry'
}
var person = new Person();
console.log(person.name)//cherry

这个就是Person.prototype = {name : ‘cherry’ }改变了原型对象思维指向,让它的值为cherry,然后再实例化一个对象的时候再调用都早函数的时候就会查找__proto__的Person.prototype为cherry。

d,.改变原型和改变原型属性的值综合起来:

Person.prototype.name = 'sunny';
function Person(){
//var this = {__proto__ : Person.prototype}
}
Person.prototype = {  //改变原型    
    name : 'cherry'
}
Person.prototype.name = 'sunny';//改变原型属性的值
var person = new Person();
console.log(person.name)//sunny

在这里插入图片描述

首先是__proto__和 Person.prototype一起指向一个空间值是sunny
然后Person.prototype指向另外一个空间值是cherry,
然后再把第二次Person.prototype指向的空间的内容替换为sunny,最后实例化对象的时候,发现自己没有这个name属性,
根据__proto__ 去原型里面查找,找到的就是第二次指向的空间里里面的值sunny。

1.每当代码读取某个对象的某个属性的时候,都会执行一次搜索。首先从对象实例本身开始,如果在实例中找到了该属性,则返回该属性的值,
2.如果没有找到,则顺着原型链指针向上,到原型对象中去找,如果如果找到就返回该属性值。
3.如果为对象实例添加了一个属性,与原型中属性同名,该属性会屏蔽掉原型中的同名属性
4.使用delete可以删除自己实例中的属性,但是原型中的属性是删除不了的。

原型链

可以把对象串联起来的链式结构

function CreateDog(name,color){
	this.name = name;
	this.color = color;
}

CreateDog.prototype.say = function(){
	console.log(this.color + this.name + '在叫!');
};

var dog  = new CreateDog('萨摩耶','白色');
dog.say();

访问原型的方法

1.通过构造函数来访问
console.log(CreateDog.prototype);//Object { say: say(), … }
2. 通过实例化的对象来访问
console.log(dog.proto);//Object { say: say(), … }

js 在创建对象(任何对象,普通对象和函数对象)的时候,都有一个__proto__的属性,
这个属性用于指向创建他的函数对象的原型对象prototype
console.log(dog.proto === CreateDog.prototype);//true

同样的,CreateDog.prototype 对象也有一个__proto__ 指向创建他的函数的原型对象 (object)的prototype

console.log(CreateDog.prototype.proto === Object.prototype);//true

Object.prototype 也有一个__proto__ 指向null

console.log(Object.prototype.proto === null);//true

原型链 特点是:__proto__ 属性

Object.prototype是原型链的终端

字面量创建对象的方法其实和new Object()的方法是一模一样的。

var obj = {};
var obj1 = new Object();

它们是相等的。

obj.__proto__ ---->Object.prototype
obj1.__proto__ ---->Object.prototype
Person.prototype = {} ------->Object.prototype
function Person(){
}

函数的原型就是一个字面量的形式,所以原型链的终端就是Object.prototype

利用Object.create(原型)创建一个对象*

//var obj = Object.create(原型)
 Person.prototype.name = "sunny";
 function Person() {

 }
 var person = Object.create(Person.prototype);
 console.log(person.name);//sunny

在这里插入图片描述
绝大多数对象的最终都会继承于Object.prototype,但是也有不继承的情况

Object.create(原型)里面的"原型"必须是一个Object对象或者空值。(放原始值会报错)

var obj = Object.create(null);
var obj1 = Object.create(123);//报错

现在创建一个对象,把null放进去,发现这个对象什么属性都没有了,没原型了。
在这里插入图片描述
调用toString()不行,它自己没有toString方法,它也没原型连__proto__属性都没有,所以根本找不到toString方法,就会报错。

人为加上_proto_属性,系统不认可
在这里插入图片描述

所以说是绝大多数对象继承于Object.prototype,不是所有的对象,因为现在的这个Object.create(null)创建的对象根本没有继承属性,连原型都没有。

toString()方法
  • undefined和null不能调用tostring方法。
  var num = 123;
    num.toString()
  • 可以调用tostring()方法,因为数字可以通过包装类来一层层访问,包装类肯定是对象,然后对象的原型链的终端是Object,它有tostring方法。

  • 但是undefined和null也不是对象,也没有包装类,他就是原始值,没有原型,不可能可以调用tostring()方法。

数字num调用toString()方法的原理:

var num = 123;
//num.toString();------->new Number(num).toString();
//number重写toString
Number.prototype.toString = function() {}
//Number.prototype.__proto__ = Object.prototype
//Object.prototype.toString = function () {}

首先Object.prototype上有一个toString方法,每一个继承Object.prototype的都可以调用,但是他们自己也重写了这个方法,就是Number.prototype.toString = function() {},每次调用toString()就是调用的自己重写的toString()方法,方便打印出自己想要的结果。

document.write()本质上是调用tostirng方法

var obj = Object.create(null);
document.write(obj);//会报错
document.write(obj.toString());//会报错,因为它没有原型,更不会有toString()方法
-------------------------------------------------------
//现在人为的加上toString()方法
var obj = Object.create(null);
obj.toString = function() {
return '访问';
}
document.write(obj);//访问
document.write(obj.toString());//访问
//证明document.write()本质上是调用tostirng方法

在这里插入图片描述

call/apply

call() 会改变方法内部this的指向,指向第一个参数,后面的参数是正常传实参。
apply()第一个参数同样是指向的对象,但实参只能传一个数组形式的。
即:

  • call需要把实参按照行形参的个数传进去。

  • apply需要把实参放进一个arguments传进去。

  • 两个方法的第一个参数必须是对象本身

/*call()方法*/
function.call(thisObj[, arg1[, arg2[, [,...argN]]]]);

call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法

/*apply()方法*/
function.apply(thisObj[, argArray])

apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。

  • 从定义中可以看出,call和apply都是调用一个对象的一个方法,用另一个对象替换当前对象。
  • 而不同之处在于传递的参数,apply最多只能有两个参数——新this对象和一个数组argArray,如果arg不是数组则会报错TypeError;
  • apply以数组传递参数,call独立传递。
  • call则可以传递多个参数,第一个参数和apply一样,是用来替换的对象,后边是参数列表。 基本语法:
function myFunction(a, b) {
    return a * b;
}
myObject = myFunction.call(myObject, 10, 2);     // 返回 20
function myFunction(a, b) {
    return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray);  // 返回 20

在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。

在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。
在这里插入图片描述

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