简易jQuery
jQuery方式一般是$(selector).fn()
思路一
const hQuery=function(selector){
}
hQuery.prototype={
constructor: hQuery,
addclass:function(){
console.log('add class')
}
}
let $node =new hQuery()
$node.addclass()
功能上可以实现,但每次使用需要手动创建新的实例,跟我们日常使用方法不一致,需要重新改造
思路二
想要实现如下使用办法
window.$ = hQuery
$().addClass
$()运行之后必须返回一个对象才会有addClass方法
var hQuery = function(selector) {
return new hQuery(selector)
}
hQuery.fn = hQuery.prototype = {
addClass: function() {
console.log('add Class')
}
}
window.$ = hQuery
var $node = $('div')
$node.addClass('active')
但是这样书写会报错Uncaught RangeError: Maximum call stack size exceeded,原因就是new hQuery的时候执行了hQuery函数出现循环调用.所以不能直接new hQuery,得换个方式.
思路三
不直接new hQuery,那么我们再给原型添加一个方法
var hQuery = function(selector) {
return new hQuery.fn.init(selector)
}
hQuery.fn = hQuery.prototype = {
init: function() {
console.log(this)
},
addClass: function() {
console.log(this)
console.log('add Class')
}
}
window.$=hQuery
$('div').addClass
执行hQuery,返回new一个新函数得到的对象(这个对象是init函数的实例),这个新函数放hQuery的原型对象里面,但是运行之后会报如下错误VM121:16 Uncaught TypeError: $(...).addClass is not a function
,原因是返回的新对象没有addClass属性;
ps:注意引擎会首先寻找init上面有没有addClass属性,没有的话会寻找init原型(init.prototype)上有没有addClass属性
思路四
var hQuery = function(selector) {
return hQuery.fn.init(selector)
}
hQuery.fn = hQuery.prototype = {
init: function() {
console.log(this)
return this
},
addClass: function() {
console.log(this)
console.log('add Class')
}
}
window.$ = hQuery
$('div').addClass('active')
没创建新对象,共用hQuery.prototype
console.log( $('div') === $('span') )
换种写法, 这时候不报错了,但也不符合我们的需求。$()得到的结果是同一个对象。为什么需要创建新的对象?因为js对象是引用类型,如果共用对象,那么我们修改其中其中的属性会影响到其他使用这个对象的变量
思路五
var hQuery = function(selector) {
return new hQuery.fn.init(selector)
}
hQuery.fn = hQuery.prototype = {
init: function() {
console.log(this)
},
addClass: function() {
console.log(this)
console.log('add Class')
}
}
hQuery.fn.init.prototype=hQuery.fn//核心代码
window.$=hQuery
$('div').addClass
基本雏形框架基本完成;
类数组对象
- $(selector)返回一个类数组对象,类似于{0:node0,1:node1,length:2}
- 对init函数重构
let nodes=document.querySelectorAll(selector);
nodes.forEach((node,index)=>{
this[index]=node;
})
this.length=nodes.length;
原型添加方法
我们后续会对原型添加方法,直接在原型上添加方法有一定隐患:例如修改了其他已经定义的方法,尤其是多人协作的过程中
我们为原型上添加一个方法,用于随时给原型添加,而不必回到一开始定义原型时的方法
extend:function(obj){
for (let key in obj){
this[key]=obj;
}
为原型添加方法示例
hQuery.fn.extend({
get:function(){
console.log('get')
}
})
addClass方法
一开始使用 Object.values()获取类数组对象dom节点,但是有一个问题,我们为类数组对象添加了length属性,这样便利的时候会获取到length
属性,这个属性显然是没有classList
属性的(就是个数字),我们当然可以使用判断把它过滤掉
Object.values(this).forEach(node=>{
if(typeof(node)!=='number'){
node.classList.add(cls)
}
})
改用array.from实现
Array.from(this).forEach(node => node.classList.add(cls))
完整代码
const hQuery = function(selector) {
return new hQuery.fn.init(selector)
}
hQuery.fn = hQuery.prototype = {
init: function(selector) {
let nodes=document.querySelectorAll(selector);
nodes.forEach((node,index)=>{
this[index]=node;//设置新对象属性
})
this.length=nodes.length;//设置新对象属性:length
console.log('hahah')
// return nodes
},
addClass: function(cls) {
console.log('add Class')
console.log(this)
console.log(Array.from(this))
Object.values(this).forEach(node=>{
if(typeof(node)!=='number'){
node.classList.add(cls)
}
})
// Array.from(this).forEach(node => node.classList.add(cls))
// return this;
},
extend:function(obj){
for (let key in obj){
this[key]=obj;
}
}
}
hQuery.fn.init.prototype=hQuery.fn//核心代码
window.$=hQuery;
hQuery.fn.extend({
get:function(){
console.log('get')
}
})
$('.demo').addClass('red')
$('.demo').addClass('yellow')