创建函数的方法
如果有2个以上的函数的话,用对象字面量的方式创建函数,会导致代码的重复所以可以用工厂模式以及构造函数和原型的模式来创建函数。
工厂模式
function creatObject(name,age){
var obj=new Object();//创建对象
obj.name=name;
obj.age=age;
obj.run=function (){
ruturn this.name+this.age;//添加属性和方法
}
return obj;//返回对象
}
var obj1=creatObject('lee',18)
alert(boj1,run())
工厂模式的缺点是不能识别对象的引用
构造函数(可以识别对象的引用)
function Box(name,age){
this.name=name;
this.age=age;
this.run=function(){
this.name+this.age
}
}
var box=new Box('Lee',100)
构造函数没有显示创建对象后台自动创建var obj()
this 相当于box
不需要返回对象引用,后台自动返回。
构造函数也是函数,但函数名需要大写
必须new构造函数
除了new调用外还可使用对象冒充调用:
var o=new object();
Box.call(o,'Lee',18);
alert(p.run());
原型
为原型添加属性和方法有两种方式。
function Box(){}//构造函数里面什么都没有,如果有的话叫做实例属性和方法。
box.prototype.name='Lee';
box.prototype.age=18;
box.prototype.run=fucntion(){};
或者:
box.prototype={
name:'Lee',
age:18,
run: function(){}
}
var box=new Box()
console.log(box.name)
构造函数方法与原型方法
- 如果是构造函数的方法则不同的实例化,它们的方法的地址是不一样的,如果有2个实例,则开辟两块地址。
如果是原型方法则它们的地址是共享的即box1和box2占用一块内存。 - 每个函数都有一个prototype(原型)属性,这个属性也是一个对象,使用原型可以让所有的对象实例共享它所有的属性和方法。
instanceof和typeof的区别
两者都用来判断一个变量是否为空,或者是什么类型。
typeof
它返回的是一个字符串,该字符串说明运算数的类型。一般只能返回number boolean string function object undefind
可以用typeof来判断一个变量是否存在,如
if(typeof a!='undefind'){alert('ok')},而不要使用if(a),因为如果a不存在则会出错(未声明)
对于Array, null等特殊对象使用typeof一律返回object,这正是typeof的局限性。
instanceof
用于判断一个变量是否是某个对象的实例,如var a=new Array();alert(a instanceof Array)
会返回true,就是arguments如果用instanceof检测就发现它不是数组的实例,它可以检测对象中的几种数据类型。
constructor
对象里面有constructor属性,即box=new Box()
中,box.constructor指向Box。构造器constructor相当于工厂,而p相当于工厂的模具。
伪类
var Person={
this.name:'cj',
this.age:18
}
p=new Person();
p的值和构造函数Person的值无关。因为改变person的值,但是p.name的值没有变。例如:p.constructor={name:'cc'}
但是p.name
仍是''cj'所以构造函数是伪类,所以p与原型之间的关系不是通过函数而是通过__proto__
实现的。
读取属性
p.name读取属性的时候,先从本身的私有属性去找,如果没有,因为p是对象所以有__proto__
属性,又因为该属性指向构造函数的 prototype
属性,所以相当于到构造函数的prototype中去找看有没有要查找的属性,如果没有,又因为原型也是对象,所以也有__proto__
属性,指向构造函数Function的原型,如果没有则到构造函数Object的原型中去找,最后Object的__proto__
指向null。
this
this书写在哪个地方决定this指向的值。
- this可以写在全局,指的是window.
- this可以写在函数中。
- 写在事件中
var ele=document.getElementById('id')
ele.addEventListener('click',function(){
console.log(this)//此处的this指向事件绑定的元素所以此处this指向ele.
})
- this写在对象的方法中
var o={
name:'cj',
print:function(){
console.log(this.name)
}
}
o.print()//this指向的是o,因为点号前面是o,证明是o调用的所以是o。
- this写在函数中
function f(){
this.name='ccc'
}
f()//此处的this指向的是window因为window.f()所以是window调用的。
var b=100;
function fn(){
var b=1;
console.log(this.b)
}//因为this先在函数内部找,没有的话则代表window,所以window.b则代表全局。
bind方法
bind任何函数都有bind方法,返回一个新的函数,this改变了函数内部的this为传入的第一个参数。
var obj={
name:'Byron',
fn:function(){
console.log(this)
}
}
var obj3={a:3}
var fn3=obj.fn.bind(obj3)此时this指的是obj3
延伸:
document.addEventListener('click',function(){
console.log(this)
setTimeout(function(){
console.log(this)
}.bind(this),200)//此时this指向的是document
},false)
改变this指向,用call和apply方法
call和apply的方法类似。
var o={
name:'cj',
print:function(){
console.log(this.name)
}
}
function log(a){
console.log(this[a])
}
改变log中的this指向可以用log.call(o,'name')
所以输出的是cj。表示log里面的this指向o对象,并且赋值为''name'。
用另一种方法的话就是log.apply(o,['name'])
,只不过第二个参数用数组表示,把所有的参数放进数组中。
还要一点要注意:
function p(){
this.name='111'
}
var o={};
p.call(o)//p.call(o)相当于o.p(),则输出o.name为''111',也就是当传入apply内为空对象的时候相当于此对象调用函数。
new的自定义实现
当创建构造函数的时候new的作用是什么呢?
function Person(name,age){
this.name=name;
this.age=age;
}
var p1=new Person('sj',18)
如果自己实现new的话是怎样的呢?
function New(f){
return function(){
var o={'__proto__':prototype}//此处的目的是与构造函数中两个值等相吻合,其实O为空的话也能输出值
f.apply(o,arguments);
return o
}
}
var p2=New (person)('xx',18)
p2.name==>'xx'
p2第一次调用的时候返回的是匿名函数第二次调用的时候传入参数并且返回的是对象。
总结原型
- 通过prototype可以把共有的属性放到一块去。
- 给继承实现了前提
详见读取属性 - 伪类的概念 具有欺骗性
- -原型的实时性,几乎所有的对象都是通过传引用的方式来传递的,因此我们的所创建的对象实例中没有原型的副本,这也意味着我们可以随时修改prototype属性,并且由同一构造器创建的所有对象的prototype属性也会随时改变(甚至还会影响在修改之前的已经创建了的那些对象)
实例
-
OOP 指什么?有哪些特性
oop指的是面向对象编程,是用抽象的方式创建基于现实世界模型的一种编程模式。它使用先前建立的范例,包括继承、多态和封装三大特性。今天流行的编程语言都支持面向对象编程。
-
通过构造函数的方式创建一个拥有属性和方法的对象
function Person(name,age){
this.name=name;
this.age=age;
this,sayName=function(){
alert(this.name+''+this.age);
}
}
var person=new Person('peter',24)
-
prototype 是什么?有什么特性
prototype指的是原型。每创建一个函数,都会有一个prototype属性,这个属性是一个指针,用来指向函数的原型对象。prototype就是通过调用构造函数而创建的那个对象实例的原型对象。
-
画出如下代码的原型图
function People (name){
this.name = name;
this.sayName = function(){
console.log('my name is:' + this.name);
}
}
People.prototype.walk = function(){
console.log(this.name + ' is walking');
}
var p1 = new People('饥人谷');
var p2 = new People('前端');
-
创建一个 Car 对象,拥有属性name、color、status;拥有方法run,stop,getStatus
function Car(name,color,status){
this.name=name;
this.color=color;
this.status=status;
Car.prototype.run=function(){};
Car.prototype.stop=function(){};
Car.prototype.getStatus=function(){};
}
var car=new Car('BMW','white','stop')
-
创建一个 GoTop 对象,当 new 一个 GotTop 对象则会在页面上创建一个回到顶部的元素,点击页面滚动到顶部。拥有以下属性和方法
1.ct
属性,GoTop 对应的 DOM 元素的容器
2.target
属性, GoTop 对应的 DOM 元素
3.bindEvent
方法, 用于绑定事件
4.createNode
方法, 用于在容器内创建节点
demo
-
apply、call 、bind有什么作用,什么区别
1、三者都是用来改变函数的this对象的指向的;
2、三者第一个参数都是this要指向的对象,也就是想指定的上下文;
3、三者都可以利用后续参数传参;
4、bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
5、当希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。
-
代码输出
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()//代码输出'john:hi!',因为this指向最后调用它的对象,最后调用它的对象是john所以this指的是firstName
-
代码输出
func()
function func() {
alert(this)
}//此处代码输出的是window。因为this会在函数内查找this,找不到会向函数外查找,最终找到this指向的是window。
-
代码输出
document.addEventListener('click', function(e){
console.log(this);//代码输出document
setTimeout(function(){
console.log(this);
}, 200);
}, false);//此处代码输出window
-
代码输出
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)//此处输出‘john’,因为call里面的第一个参数是john,所以func的this指向'john'
-
查找问题并修改
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指的是$btn
this.showMsg();
})
},
showMsg: function(){
console.log('饥人谷');
}
}
修改后:
var module= {
bind: function(){
var that=this
$btn.on('click', function(){
console.log(this) //this指的是$btn
that.showMsg();//此处的that指向的是module
})
},
showMsg: function(){
console.log('饥人谷');
}
}
原型链相关问题
-
解释Person、 prototype、proto、p、constructor之间的关联.
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log('My name is :' + this.name);
}
var p = new Person("若愚")
p.sayName();
关联: p.__ proto__===Person.prototype
p.constructor===Person
p.__ proto__.constructor===Person
Person.prototype===Person
-
画出原型链问题
对于p.toString先从本身的私有属性中查找,如果没有找到,会从p.__ proto__也就是构造函数的prototype中查找,如果仍旧没有,因为构造函数Person的prototype也是对象,也拥有__ proto__属性即Object.prototype中查找,最终找到toString方法。
原型链就是由各级对象的__ proto__属性连续继承形成的链式结构,用来控制属性的使用顺序:自己有就不用父级的,自己没有才从父级找。如果父级没有,继续沿原型链向上找。如果到顶级对象都没有才返回undefined。
-
获取字符串中频率最高的字符
String.prototype.getMostOften=function(){
var obj={}
for(var i=0;i<this.length;i++){
var k=this[i]
if(!obj[k]){
obj[k]=1;
}else{
obj[k]++
}
}
var max=0,
val
for(var key in obj){
if(obj[key]>max){
max=obj[key];
val=key;
}
}
return val;
}
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();//因为是str调用的函数,所以this指的是字符串str
console.log(ch); //d , 因为d 出现了5次
-
instanceof
instanceof用于判断一个对象是否是某个构造函数的实例。内部逻辑的实现是通过检测构造函数的原型是否在对象的原型链上。
function a(){}
var b=new a()
b instanceof a//true
过程:先看b.__ proto__是否等于a.prototype,如果相等则返回true,否则返回false。